From patchwork Mon Aug 6 18:04:31 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Qiuyu Xiao X-Patchwork-Id: 954118 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="cIT10vJQ"; dkim-atps=neutral Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 41kltn1fgkz9rvt for ; Tue, 7 Aug 2018 04:05:37 +1000 (AEST) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id E227BCC9; Mon, 6 Aug 2018 18:05:05 +0000 (UTC) X-Original-To: ovs-dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id 1CE2ACC6 for ; Mon, 6 Aug 2018 18:05:04 +0000 (UTC) X-Greylist: whitelisted by SQLgrey-1.7.6 Received: from mail-pg1-f196.google.com (mail-pg1-f196.google.com [209.85.215.196]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id A8BE5735 for ; Mon, 6 Aug 2018 18:05:03 +0000 (UTC) Received: by mail-pg1-f196.google.com with SMTP id h12-v6so6574433pgs.3 for ; Mon, 06 Aug 2018 11:05:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=w85i38FUR87PNS9NHY42bJ3P+2Ipvp2UZPZJRWQDCtI=; b=cIT10vJQZkjq8m7MDSmG9dGxVtyvjblKnaueST69wYY9W15XVAWPc9Cy+mqsLlIyGh /MMXHzcYh6g26zpsjq5PPom5zCGLPGTjjEQU+50QeUQGZKr7PHx8NhNZEjNAK0VvftF3 MZf4vBZIkdmN5atZTYeUBiHnW4poIm2hpAtsujPU36O44iizGSfP4UdNAwkIBMVxAG1o Xe58XtkkfmnC1h+5Ci3v3N8Z6ZlrasPy1dre5rs9h/uzuHghX4yxnWwH5fZwFGCB11nB wnh4xWfYwzgeIxosTakVb8W/4Lmq7XYnQAZTeWc56uUM+ZhOXCWSJymXGmIuoMiJLt9a bqEw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=w85i38FUR87PNS9NHY42bJ3P+2Ipvp2UZPZJRWQDCtI=; b=MMWw5SvOAfUw1nyThpSgip8Ed8YnrrzwmQa4iBqu2U6+z5/XZ2Kb31zhsSF60JFPpi yRiCd8KGPJA0lWUU0VTf6FF7nxbUA3WgyaLFbJ2gKc/kRzh6QUMZD5jKQp1MHH+KVj7h TcFTx8THMelQBNn+SldxrwJFwtyISvv2Rdu5Ilh9OfPMewZwRUI20V/7bFG/s4JxTS6N zf8H8hUoiXBO/2hpwSpbZGbfWqjz6NLeWAipxrvdYqQ5lUh+olSvJHbmlwtgBCg1l9UG ymyptGvMgxZxMfrS27n5CpX8LOM8FA4zGGX2KKTPayrLSpqNFg2UBpCO6Tv2nxGsvzsJ 0x3Q== X-Gm-Message-State: AOUpUlEzi7y8PGsdGeNMJm9jbWCM3lCXvY5n+QtSMeMG2weMLQXsAIhy tWbYgRM97q7gP6W8wNPBkaqDbDA9 X-Google-Smtp-Source: AAOMgpd+Tr03iTSy8MoLuZaiC4e35spRKOR1BZPz6n2q4eGk5B6A8JqS23uyqUK8aHBMlE+Cfp0IAw== X-Received: by 2002:a63:790b:: with SMTP id u11-v6mr15318669pgc.111.1533578703054; Mon, 06 Aug 2018 11:05:03 -0700 (PDT) Received: from vm1.eng.vmware.com ([66.170.99.1]) by smtp.gmail.com with ESMTPSA id p64-v6sm23453965pfa.47.2018.08.06.11.05.02 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 06 Aug 2018 11:05:02 -0700 (PDT) From: Qiuyu Xiao To: ovs-dev@openvswitch.org Date: Mon, 6 Aug 2018 11:04:31 -0700 Message-Id: <20180806180439.16559-2-qiuyu.xiao.qyx@gmail.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20180806180439.16559-1-qiuyu.xiao.qyx@gmail.com> References: <20180806180439.16559-1-qiuyu.xiao.qyx@gmail.com> X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, FREEMAIL_FROM, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [PATCH v5 1/9] datapath: add transport ports in route lookup for geneve X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org This patch adds transport ports information for route lookup so that IPsec can select geneve tunnel traffic to do encryption. Signed-off-by: Qiuyu Xiao Reviewed-by: Greg Rose Tested-by: Greg Rose --- datapath/linux/compat/geneve.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/datapath/linux/compat/geneve.c b/datapath/linux/compat/geneve.c index 435a23fb7..95a665ddd 100644 --- a/datapath/linux/compat/geneve.c +++ b/datapath/linux/compat/geneve.c @@ -836,7 +836,8 @@ free_dst: static struct rtable *geneve_get_v4_rt(struct sk_buff *skb, struct net_device *dev, struct flowi4 *fl4, - struct ip_tunnel_info *info) + struct ip_tunnel_info *info, + __be16 dport, __be16 sport) { bool use_cache = ip_tunnel_dst_cache_usable(skb, info); struct geneve_dev *geneve = netdev_priv(dev); @@ -850,6 +851,8 @@ static struct rtable *geneve_get_v4_rt(struct sk_buff *skb, memset(fl4, 0, sizeof(*fl4)); fl4->flowi4_mark = skb->mark; fl4->flowi4_proto = IPPROTO_UDP; + fl4->fl4_dport = dport; + fl4->fl4_sport = sport; if (info) { fl4->daddr = info->key.u.ipv4.dst; @@ -895,7 +898,8 @@ static struct rtable *geneve_get_v4_rt(struct sk_buff *skb, static struct dst_entry *geneve_get_v6_dst(struct sk_buff *skb, struct net_device *dev, struct flowi6 *fl6, - struct ip_tunnel_info *info) + struct ip_tunnel_info *info, + __be16 dport, __be16 sport) { bool use_cache = ip_tunnel_dst_cache_usable(skb, info); struct geneve_dev *geneve = netdev_priv(dev); @@ -911,6 +915,8 @@ static struct dst_entry *geneve_get_v6_dst(struct sk_buff *skb, memset(fl6, 0, sizeof(*fl6)); fl6->flowi6_mark = skb->mark; fl6->flowi6_proto = IPPROTO_UDP; + fl6->fl6_dport = dport; + fl6->fl6_sport = sport; if (info) { fl6->daddr = info->key.u.ipv6.dst; @@ -1005,13 +1011,13 @@ static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev, goto tx_error; } - rt = geneve_get_v4_rt(skb, dev, &fl4, info); + sport = udp_flow_src_port(geneve->net, skb, 1, USHRT_MAX, true); + rt = geneve_get_v4_rt(skb, dev, &fl4, info, geneve->dst_port, sport); if (IS_ERR(rt)) { err = PTR_ERR(rt); goto tx_error; } - sport = udp_flow_src_port(geneve->net, skb, 1, USHRT_MAX, true); skb_reset_mac_header(skb); iip = ip_hdr(skb); @@ -1097,13 +1103,13 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev, } } - dst = geneve_get_v6_dst(skb, dev, &fl6, info); + sport = udp_flow_src_port(geneve->net, skb, 1, USHRT_MAX, true); + dst = geneve_get_v6_dst(skb, dev, &fl6, info, geneve->dst_port, sport); if (IS_ERR(dst)) { err = PTR_ERR(dst); goto tx_error; } - sport = udp_flow_src_port(geneve->net, skb, 1, USHRT_MAX, true); skb_reset_mac_header(skb); iip = ip_hdr(skb); @@ -1232,13 +1238,17 @@ int ovs_geneve_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb) struct geneve_dev *geneve = netdev_priv(dev); struct rtable *rt; struct flowi4 fl4; + __be16 sport; #if IS_ENABLED(CONFIG_IPV6) struct dst_entry *dst; struct flowi6 fl6; #endif + sport = udp_flow_src_port(geneve->net, skb, + 1, USHRT_MAX, true); + if (ip_tunnel_info_af(info) == AF_INET) { - rt = geneve_get_v4_rt(skb, dev, &fl4, info); + rt = geneve_get_v4_rt(skb, dev, &fl4, info, geneve->dst_port, sport); if (IS_ERR(rt)) return PTR_ERR(rt); @@ -1246,7 +1256,7 @@ int ovs_geneve_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb) info->key.u.ipv4.src = fl4.saddr; #if IS_ENABLED(CONFIG_IPV6) } else if (ip_tunnel_info_af(info) == AF_INET6) { - dst = geneve_get_v6_dst(skb, dev, &fl6, info); + dst = geneve_get_v6_dst(skb, dev, &fl6, info, geneve->dst_port, sport); if (IS_ERR(dst)) return PTR_ERR(dst); @@ -1257,8 +1267,7 @@ int ovs_geneve_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb) return -EINVAL; } - info->key.tp_src = udp_flow_src_port(geneve->net, skb, - 1, USHRT_MAX, true); + info->key.tp_src = sport; info->key.tp_dst = geneve->dst_port; return 0; } From patchwork Mon Aug 6 18:04:32 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Qiuyu Xiao X-Patchwork-Id: 954120 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="AoRsnF+u"; dkim-atps=neutral Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 41klvJ14b8z9s0R for ; Tue, 7 Aug 2018 04:06:04 +1000 (AEST) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 2AFEDCD3; Mon, 6 Aug 2018 18:05:08 +0000 (UTC) X-Original-To: ovs-dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id 5C880CC8 for ; Mon, 6 Aug 2018 18:05:06 +0000 (UTC) X-Greylist: whitelisted by SQLgrey-1.7.6 Received: from mail-pf1-f196.google.com (mail-pf1-f196.google.com [209.85.210.196]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id F3BAD735 for ; Mon, 6 Aug 2018 18:05:05 +0000 (UTC) Received: by mail-pf1-f196.google.com with SMTP id b11-v6so7237247pfo.3 for ; Mon, 06 Aug 2018 11:05:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=HJvaFa4feoRojbHlLXda2/DnUfD1HI5ylVSg7Sdi7kA=; b=AoRsnF+uUm8yn2WSPf00FsPcfcOVzWUB9/mXIjYl2lIxuAVL2WxwA5CK3BbTVnSIMH wrxYmboW3dpOL/tPayPAFZQ2UvjXxTTfeZlg6fCJXXcnc+ZgcqVbZg3iaXyNNZhszGyP GFHju4DiI0j4Q+wgKTvSUhhLXHiEnc2sV8D/mGFQdr+kLj8LQ/luDtE9l5xXQrYUg+oM CZa/3OM8KJ9mtSScFCL5mVGQoBNoq3nlJn1RiH9lzB/Cr2nkcWa8WVc8ol3Dqryk7rZ5 lWAXBDyBRrfpj7cPPc3EekvZS5pkYkFFZ3a6O1SwmXA9mEPyLo65mn5tbXzexA7VfIoH HVKA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=HJvaFa4feoRojbHlLXda2/DnUfD1HI5ylVSg7Sdi7kA=; b=KOatH/G0s8jhzr5Y0rltJ1HHLVaghasPH7KcHP4WbQIkjI7H0j4oC+y1pkTu6btRTD fiUIDj99NRSgYL/kLyvSbzBhJIvpgOOoBCU2XFnkrMJO8M3F0vR2/IPEzBz+kxmVMLLg yrcK8dviSIEGCJPIMcjk89ZeVv7mTpMIp9ToBmF6OHSsYmFpfqlX3CPLcQXyXapEu717 9Ig1emKtDAuhFIZBTVcSL1YEUiFsXarc1IbNxAqPcp4bgzI7sc5CU/yfXNLHS6Ugp3mT XctKG9llcVsGxqjv0TruSThxl3XV6ixBo+FdiZAxi/jAJY5mPS/wbBNvUhJ7f8PCXoZJ weOA== X-Gm-Message-State: AOUpUlGZYQrd1LoAMmqSg3vnDRXC/SRxErQ6GcLVE4dOnWSV18ZLtNCa zyeoo5RoayGSgiDH1Hq9XJ1EKgYJ X-Google-Smtp-Source: AAOMgpduEt3obh6w8ohS8+bf3lMtRRimkdaL3UEHHWLKt9G0upxNd6MFsNsYxYqvWZrIL/Low9wwhg== X-Received: by 2002:a63:3e4d:: with SMTP id l74-v6mr15763883pga.355.1533578705443; Mon, 06 Aug 2018 11:05:05 -0700 (PDT) Received: from vm1.eng.vmware.com ([66.170.99.1]) by smtp.gmail.com with ESMTPSA id p64-v6sm23453965pfa.47.2018.08.06.11.05.04 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 06 Aug 2018 11:05:04 -0700 (PDT) From: Qiuyu Xiao To: ovs-dev@openvswitch.org Date: Mon, 6 Aug 2018 11:04:32 -0700 Message-Id: <20180806180439.16559-3-qiuyu.xiao.qyx@gmail.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20180806180439.16559-1-qiuyu.xiao.qyx@gmail.com> References: <20180806180439.16559-1-qiuyu.xiao.qyx@gmail.com> X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, FREEMAIL_FROM, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [PATCH v5 2/9] datapath: add transport ports in route lookup for vxlan X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org This patch adds transport ports information for route lookup so that IPsec can select vxlan tunnel traffic to do encryption. Signed-off-by: Qiuyu Xiao Reviewed-by: Greg Rose Tested-by: Greg Rose --- datapath/linux/compat/vxlan.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/datapath/linux/compat/vxlan.c b/datapath/linux/compat/vxlan.c index 7f5d5ce64..b850fdd44 100644 --- a/datapath/linux/compat/vxlan.c +++ b/datapath/linux/compat/vxlan.c @@ -896,6 +896,7 @@ out_free: static struct rtable *vxlan_get_route(struct vxlan_dev *vxlan, struct sk_buff *skb, int oif, u8 tos, __be32 daddr, __be32 *saddr, + __be16 dport, __be16 sport, struct dst_cache *dst_cache, const struct ip_tunnel_info *info) { @@ -918,6 +919,8 @@ static struct rtable *vxlan_get_route(struct vxlan_dev *vxlan, fl4.flowi4_proto = IPPROTO_UDP; fl4.daddr = daddr; fl4.saddr = *saddr; + fl4.fl4_dport = dport; + fl4.fl4_sport = sport; rt = ip_route_output_key(vxlan->net, &fl4); if (!IS_ERR(rt)) { @@ -934,6 +937,7 @@ static struct dst_entry *vxlan6_get_route(struct vxlan_dev *vxlan, __be32 label, const struct in6_addr *daddr, struct in6_addr *saddr, + __be16 dport, __be16 sport, struct dst_cache *dst_cache, const struct ip_tunnel_info *info) { @@ -961,6 +965,8 @@ static struct dst_entry *vxlan6_get_route(struct vxlan_dev *vxlan, fl6.flowlabel = ip6_make_flowinfo(RT_TOS(tos), label); fl6.flowi6_mark = skb->mark; fl6.flowi6_proto = IPPROTO_UDP; + fl6.fl6_dport = dport; + fl6.fl6_sport = sport; #ifdef HAVE_IPV6_DST_LOOKUP_NET err = ipv6_stub->ipv6_dst_lookup(vxlan->net, @@ -1090,6 +1096,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, rdst ? rdst->remote_ifindex : 0, tos, dst->sin.sin_addr.s_addr, &src->sin.sin_addr.s_addr, + dst_port, src_port, dst_cache, info); if (IS_ERR(rt)) { netdev_dbg(dev, "no route to %pI4\n", @@ -1149,6 +1156,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, rdst ? rdst->remote_ifindex : 0, tos, label, &dst->sin6.sin6_addr, &src->sin6.sin6_addr, + dst_port, src_port, dst_cache, info); if (IS_ERR(ndst)) { netdev_dbg(dev, "no route to %pI6\n", @@ -1439,7 +1447,8 @@ int ovs_vxlan_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb) return -EINVAL; rt = vxlan_get_route(vxlan, skb, 0, info->key.tos, info->key.u.ipv4.dst, - &info->key.u.ipv4.src, NULL, info); + &info->key.u.ipv4.src, + dport, sport, NULL, info); if (IS_ERR(rt)) return PTR_ERR(rt); ip_rt_put(rt); @@ -1449,7 +1458,8 @@ int ovs_vxlan_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb) ndst = vxlan6_get_route(vxlan, skb, 0, info->key.tos, info->key.label, &info->key.u.ipv6.dst, - &info->key.u.ipv6.src, NULL, info); + &info->key.u.ipv6.src, + dport, sport, NULL, info); if (IS_ERR(ndst)) return PTR_ERR(ndst); dst_release(ndst); From patchwork Mon Aug 6 18:04:33 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Qiuyu Xiao X-Patchwork-Id: 954123 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="JRn1hVct"; dkim-atps=neutral Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 41klvw1B7rz9rxx for ; Tue, 7 Aug 2018 04:06:36 +1000 (AEST) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id E6125CD4; Mon, 6 Aug 2018 18:05:09 +0000 (UTC) X-Original-To: ovs-dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id 2B871CD8 for ; Mon, 6 Aug 2018 18:05:08 +0000 (UTC) X-Greylist: whitelisted by SQLgrey-1.7.6 Received: from mail-pf1-f194.google.com (mail-pf1-f194.google.com [209.85.210.194]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id D21CB735 for ; Mon, 6 Aug 2018 18:05:07 +0000 (UTC) Received: by mail-pf1-f194.google.com with SMTP id y10-v6so7234797pfn.8 for ; Mon, 06 Aug 2018 11:05:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=cQQ9s4z5DFdsnWgfilrxx2WTQnFxyAin2IzETYn9hKc=; b=JRn1hVct++ZzFzdADBibpacJKDG3b3I3UOtklyoKELo5qPngXZ/oDTDLBFwCSmbloV M9N0hzptJYL8zqyPCx9Sdr2/K9qbNQ1eYB5JsTj5eSOVqbyUeLiJLCf3PKxWD+GF08r1 bJOAfbR9KXT3KTqmbX4GjlHMs38c1J5cVNwB2bq6cgrT8RB4bA+gJYymzzvTu86a6bgv PRgHRhLVkobavLztK4C7kSa6XrodbAm/6WYSg4Yov9a6F+brmkTA9iecHltYqboOxgzo ab2UYafs6LMUCy/nWwZ9Q6vEmqYBCJqRBZzDmqf8CbFnSSNE3kTKIXyxTGJ/eW3BkCJY 0E3w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=cQQ9s4z5DFdsnWgfilrxx2WTQnFxyAin2IzETYn9hKc=; b=rCTmYW+5df4Rijanwakwoz28OLjac7F/fRoFdClqeJANo9WmuhqAKLBudbmhsV5pPM +bSLHcDK6KIGL9E+X+Ggle4mAvbXyeZf+KDt1KDnrJhj5ExSbY6tj5VNqvTFFwd0x3pC wbBSJpbH1+hvZJhdLaJVvoeQLcv17tTBk+KLJqvWcI/TyGnOcZQ7UUA2l4o9q642adwn /F3Wqhj4kS2hYAh+9UYRvKSy3Imsr3M1Mgj2BLF0wMZYVP0dfvcJTjpfo+FCh/Owxjgw u8VWHRZf7g6nSlk885w5dydQ6VKrfO19yJSitdD/KuV1QJUmwd3s+7iZw2p8j6Nrf7eT CWbQ== X-Gm-Message-State: AOUpUlE3oOxH44qTZBE11FsKjbE28tFlhnYuaL9rPhdkciDK80QzIhHs BhZHLFBdakKhsMlF4pj1UJhjehyq X-Google-Smtp-Source: AAOMgpe6Cu4qJhE+uBU0BVsNehu1t+eSYbqLlFv3EXrmuSzdd/+DvN38uDQ6sCny6awPI5iGCGXzPA== X-Received: by 2002:a63:214f:: with SMTP id s15-v6mr15466494pgm.267.1533578707304; Mon, 06 Aug 2018 11:05:07 -0700 (PDT) Received: from vm1.eng.vmware.com ([66.170.99.1]) by smtp.gmail.com with ESMTPSA id p64-v6sm23453965pfa.47.2018.08.06.11.05.06 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 06 Aug 2018 11:05:06 -0700 (PDT) From: Qiuyu Xiao To: ovs-dev@openvswitch.org Date: Mon, 6 Aug 2018 11:04:33 -0700 Message-Id: <20180806180439.16559-4-qiuyu.xiao.qyx@gmail.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20180806180439.16559-1-qiuyu.xiao.qyx@gmail.com> References: <20180806180439.16559-1-qiuyu.xiao.qyx@gmail.com> X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, FREEMAIL_FROM, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [PATCH v5 3/9] datapath: add transport ports in route lookup for stt X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org This patch adds transport ports information for route lookup so that IPsec can select stt tunnel traffic to do encryption. Signed-off-by: Qiuyu Xiao Reviewed-by: Greg Rose Tested-by: Greg Rose --- datapath/linux/compat/stt.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/datapath/linux/compat/stt.c b/datapath/linux/compat/stt.c index fc7e74f6d..8d16c1f2e 100644 --- a/datapath/linux/compat/stt.c +++ b/datapath/linux/compat/stt.c @@ -972,7 +972,8 @@ err_free_rt: static struct rtable *stt_get_rt(struct sk_buff *skb, struct net_device *dev, struct flowi4 *fl, - const struct ip_tunnel_key *key) + const struct ip_tunnel_key *key, + __be16 dport, __be16 sport) { struct net *net = dev_net(dev); @@ -983,6 +984,8 @@ static struct rtable *stt_get_rt(struct sk_buff *skb, fl->flowi4_tos = RT_TOS(key->tos); fl->flowi4_mark = skb->mark; fl->flowi4_proto = IPPROTO_TCP; + fl->fl4_dport = dport; + fl->fl4_sport = sport; return ip_route_output_key(net, fl); } @@ -1009,14 +1012,14 @@ netdev_tx_t ovs_stt_xmit(struct sk_buff *skb) tun_key = &tun_info->key; - rt = stt_get_rt(skb, dev, &fl, tun_key); + sport = udp_flow_src_port(net, skb, 1, USHRT_MAX, true); + rt = stt_get_rt(skb, dev, &fl, tun_key, dport, sport); if (IS_ERR(rt)) { err = PTR_ERR(rt); goto error; } df = tun_key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0; - sport = udp_flow_src_port(net, skb, 1, USHRT_MAX, true); skb->ignore_df = 1; stt_xmit_skb(skb, rt, fl.saddr, tun_key->u.ipv4.dst, @@ -1818,20 +1821,22 @@ int ovs_stt_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb) struct stt_dev *stt_dev = netdev_priv(dev); struct net *net = stt_dev->net; __be16 dport = stt_dev->dst_port; + __be16 sport; struct flowi4 fl4; struct rtable *rt; if (ip_tunnel_info_af(info) != AF_INET) return -EINVAL; - rt = stt_get_rt(skb, dev, &fl4, &info->key); + sport = udp_flow_src_port(net, skb, 1, USHRT_MAX, true); + rt = stt_get_rt(skb, dev, &fl4, &info->key, dport, sport); if (IS_ERR(rt)) return PTR_ERR(rt); ip_rt_put(rt); info->key.u.ipv4.src = fl4.saddr; - info->key.tp_src = udp_flow_src_port(net, skb, 1, USHRT_MAX, true); + info->key.tp_src = sport; info->key.tp_dst = dport; return 0; } From patchwork Mon Aug 6 18:04:34 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Qiuyu Xiao X-Patchwork-Id: 954125 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="nbvBLYeH"; dkim-atps=neutral Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 41klwT0cNsz9rvt for ; Tue, 7 Aug 2018 04:07:05 +1000 (AEST) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id A09ACCE9; Mon, 6 Aug 2018 18:05:16 +0000 (UTC) X-Original-To: ovs-dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id 84415CBD for ; Mon, 6 Aug 2018 18:05:15 +0000 (UTC) X-Greylist: whitelisted by SQLgrey-1.7.6 Received: from mail-pl0-f68.google.com (mail-pl0-f68.google.com [209.85.160.68]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id E237C7C7 for ; Mon, 6 Aug 2018 18:05:12 +0000 (UTC) Received: by mail-pl0-f68.google.com with SMTP id e11-v6so5982703plb.3 for ; Mon, 06 Aug 2018 11:05:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=eb88L3wwaXsYj62hJLh+hWkjAtqWzDHcavQalmdkrq8=; b=nbvBLYeHMps53glGpffUoYE1qjZwyvl1H4MI0xuOtRdkhiHDfpQdUh4OoqRq4yOpTz C1/yPv7DTzXr+2tqFpLCgmArY8Ha71ru7C9UjUQZPXDteiOP1BXOhi4z+2j07ck/rAbd rC0StFE3g3/RRyZ9CURbExzDY2lgWis4o4EM5ShTc3QSS2LWfnHe+z4KNrRuFjsd6PLk 9iNSjqL/zhdSSk3eo649ZelPiyV/kwGkNV+VsvnQhZhL7qw76ELijkDInGhKmH0MCbTV xShXJ57EHGFjrq32QYJO6naZHnAuqALYcz9SXRtZEptVNaEus6eATDUoCQGZVVTQChgI EjdQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=eb88L3wwaXsYj62hJLh+hWkjAtqWzDHcavQalmdkrq8=; b=iijCHk/96AZ2AzHdoA1khp9b07FpseWBgHhmRZ+G/75wCcWcdPRytQ/99zJi5YsekW GU/JI6N8DNZfDRJKj8Srf15zCY6Xrj2+Y0FwzLZw30fPDx/CK4iRLXOm6QHqelM87XQP ZQ94RJYRfhVdAvP2zeB3MYIIsMLTdA1yfgdcGZvaIYWNQ0DJfGwbWpJM/Q2//lq2TV6a 3TPOdYFK4pV1HsUvhjoPITkqlihQW8ccliXxAJEyBuxe2qi03bNccYsyQ04gGJl/Oksf pCLooG9PNubv4aQCSAMW1k2Mm/NBdC98D3KXXfl9MXGfwxCOrpJUKVNCde1RwulNFlnb fMsQ== X-Gm-Message-State: AOUpUlFpqEFXGRMHxbhTOXcEffoqoRV2pdfkzl+IPApBxleNsVJE59jf i6uCrURBsKkBVBUSPS7yT3EZE1Y+ X-Google-Smtp-Source: AAOMgpdyVamR4TUOtmCmZMG//BTbD8StLz6ZVoQtJ/SlARDwYUoiz6Ey8gAUpskwDV1Gitkwxnnpgg== X-Received: by 2002:a17:902:9893:: with SMTP id s19-v6mr9898508plp.130.1533578711473; Mon, 06 Aug 2018 11:05:11 -0700 (PDT) Received: from vm1.eng.vmware.com ([66.170.99.1]) by smtp.gmail.com with ESMTPSA id p64-v6sm23453965pfa.47.2018.08.06.11.05.10 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 06 Aug 2018 11:05:10 -0700 (PDT) From: Qiuyu Xiao To: ovs-dev@openvswitch.org Date: Mon, 6 Aug 2018 11:04:34 -0700 Message-Id: <20180806180439.16559-5-qiuyu.xiao.qyx@gmail.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20180806180439.16559-1-qiuyu.xiao.qyx@gmail.com> References: <20180806180439.16559-1-qiuyu.xiao.qyx@gmail.com> X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, FREEMAIL_FROM, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Cc: Ansis Atteka Subject: [ovs-dev] [PATCH v5 4/9] ipsec: reintroduce IPsec support for tunneling X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org This patch reintroduces ovs-monitor-ipsec daemon that was previously removed by commit 2b02d770 ("openvswitch: Allow external IPsec tunnel management.") After this patch, there are no IPsec flavored tunnels anymore. IPsec is enabled by setting up the right values in: 1. OVSDB:Interface:options column; 2. OVSDB:Open_vSwitch:other_config column; 3. OpenFlow pipeline. GRE, VXLAN, GENEVE, and STT IPsec tunnels are supported. LibreSwan and StrongSwan IKE daemons are supported. User can choose pre-shared key, self-signed peer certificate, or CA-signed certificate as authentication method. Signed-off-by: Qiuyu Xiao Signed-off-by: Ansis Atteka Co-authored-by: Ansis Atteka --- Makefile.am | 1 + ipsec/automake.mk | 10 + ipsec/ovs-monitor-ipsec | 1173 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 1184 insertions(+) create mode 100644 ipsec/automake.mk create mode 100755 ipsec/ovs-monitor-ipsec diff --git a/Makefile.am b/Makefile.am index 788972804..aeb2d108f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -481,6 +481,7 @@ include tests/automake.mk include include/automake.mk include third-party/automake.mk include debian/automake.mk +include ipsec/automake.mk include vswitchd/automake.mk include ovsdb/automake.mk include rhel/automake.mk diff --git a/ipsec/automake.mk b/ipsec/automake.mk new file mode 100644 index 000000000..1e530cb42 --- /dev/null +++ b/ipsec/automake.mk @@ -0,0 +1,10 @@ +# Copyright (C) 2017 Nicira, Inc. +# +# Copying and distribution of this file, with or without modification, +# are permitted in any medium without royalty provided the copyright +# notice and this notice are preserved. This file is offered as-is, +# without warranty of any kind. + +EXTRA_DIST += \ + ipsec/ovs-monitor-ipsec +FLAKE8_PYFILES += ipsec/ovs-monitor-ipsec diff --git a/ipsec/ovs-monitor-ipsec b/ipsec/ovs-monitor-ipsec new file mode 100755 index 000000000..163b04004 --- /dev/null +++ b/ipsec/ovs-monitor-ipsec @@ -0,0 +1,1173 @@ +#!/usr/bin/env python +# Copyright (c) 2017 Nicira, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import argparse +import re +import subprocess +import sys +import copy +from string import Template + +import ovs.daemon +import ovs.db.idl +import ovs.dirs +import ovs.unixctl +import ovs.unixctl.server +import ovs.util +import ovs.vlog + + +FILE_HEADER = "# Generated by ovs-monitor-ipsec...do not modify by hand!\n\n" +SHUNT_POLICY = """conn prevent_unencrypted_gre + type=drop + leftprotoport=gre + mark={0} + +conn prevent_unencrypted_geneve + type=drop + leftprotoport=udp/6081 + mark={0} + +conn prevent_unencrypted_stt + type=drop + leftprotoport=tcp/7471 + mark={0} + +conn prevent_unencrypted_vxlan + type=drop + leftprotoport=udp/4789 + mark={0} + +""" +transp_tmpl = {"gre": Template("""\ +conn $ifname-$version +$auth_section + leftprotoport=gre + rightprotoport=gre + +"""), "gre64": Template("""\ +conn $ifname-$version +$auth_section + leftprotoport=gre + rightprotoport=gre + +"""), "geneve": Template("""\ +conn $ifname-in-$version +$auth_section + leftprotoport=udp/6081 + rightprotoport=udp + +conn $ifname-out-$version +$auth_section + leftprotoport=udp + rightprotoport=udp/6081 + +"""), "stt": Template("""\ +conn $ifname-in-$version +$auth_section + leftprotoport=tcp/7471 + rightprotoport=tcp + +conn $ifname-out-$version +$auth_section + leftprotoport=tcp + rightprotoport=tcp/7471 + +"""), "vxlan": Template("""\ +conn $ifname-in-$version +$auth_section + leftprotoport=udp/4789 + rightprotoport=udp + +conn $ifname-out-$version +$auth_section + leftprotoport=udp + rightprotoport=udp/4789 + +""")} +vlog = ovs.vlog.Vlog("ovs-monitor-ipsec") +exiting = False +monitor = None +xfrm = None + + +class XFRM(object): + """This class is a simple wrapper around ip-xfrm (8) command line + utility. We are using this class only for informational purposes + so that ovs-monitor-ipsec could verify that IKE keying daemon has + installed IPsec policies and security associations into kernel as + expected.""" + + def __init__(self, ip_root_prefix): + self.IP = ip_root_prefix + "/sbin/ip" + + def get_policies(self): + """This function returns IPsec policies (from kernel) in a dictionary + where is destination IPv4 address and is SELECTOR of + the IPsec policy.""" + policies = {} + proc = subprocess.Popen([self.IP, 'xfrm', 'policy'], + stdout=subprocess.PIPE) + while True: + line = proc.stdout.readline().strip() + if line == '': + break + a = line.split(" ") + if len(a) >= 4 and a[0] == "src" and a[2] == "dst": + dst = (a[3].split("/"))[0] + if dst not in policies: + policies[dst] = [] + policies[dst].append(line) + src = (a[3].split("/"))[0] + if src not in policies: + policies[src] = [] + policies[src].append(line) + return policies + + def get_securities(self): + """This function returns IPsec security associations (from kernel) + in a dictionary where is destination IPv4 address and + is SELECTOR.""" + securities = {} + proc = subprocess.Popen([self.IP, 'xfrm', 'state'], + stdout=subprocess.PIPE) + while True: + line = proc.stdout.readline().strip() + if line == '': + break + a = line.split(" ") + if len(a) >= 4 and a[0] == "sel" \ + and a[1] == "src" and a[3] == "dst": + remote_ip = a[4].rstrip().split("/")[0] + local_ip = a[2].rstrip().split("/")[0] + if remote_ip not in securities: + securities[remote_ip] = [] + securities[remote_ip].append(line) + if local_ip not in securities: + securities[local_ip] = [] + securities[local_ip].append(line) + return securities + + +class StrongSwanHelper(object): + """This class does StrongSwan specific configurations.""" + + STRONGSWAN_CONF = """%s +charon.plugins.kernel-netlink.set_proto_port_transport_sa = yes +charon.plugins.kernel-netlink.xfrm_ack_expires = 10 +charon.load_modular = yes +charon.plugins.gcm.load = yes +""" % (FILE_HEADER) + + CONF_HEADER = """%s +config setup + uniqueids=yes + +conn %%default + keyingtries=%%forever + type=transport + keyexchange=ikev2 + auto=route + ike=aes256gcm16-sha256-modp2048 + esp=aes256gcm16-modp2048 + +""" % (FILE_HEADER) + + CA_SECTION = """ca ca_auth + cacert=%s + +""" + + auth_tmpl = {"psk": Template("""\ + left=$local_ip + right=$remote_ip + authby=psk"""), + "pki_remote": Template("""\ + left=$local_ip + right=$remote_ip + leftid=$local_name + rightid=$remote_name + leftcert=$certificate + rightcert=$remote_cert"""), + "pki_ca": Template("""\ + left=$local_ip + right=$remote_ip + leftid=$local_name + rightid=$remote_name + leftcert=$certificate""")} + + def __init__(self, root_prefix): + self.CHARON_CONF = root_prefix + "/etc/strongswan.d/ovs.conf" + self.IPSEC = root_prefix + "/usr/sbin/ipsec" + self.IPSEC_CONF = root_prefix + "/etc/ipsec.conf" + self.IPSEC_SECRETS = root_prefix + "/etc/ipsec.secrets" + self.conf_file = None + self.secrets_file = None + + def start_ike_daemon(self): + """This function starts StrongSwan.""" + f = open(self.CHARON_CONF, "w") + f.write(self.STRONGSWAN_CONF) + f.close() + + f = open(self.IPSEC_CONF, "w") + f.write(self.CONF_HEADER) + f.close() + + f = open(self.IPSEC_SECRETS, "w") + f.write(FILE_HEADER) + f.close() + + vlog.info("Starting StrongSwan") + subprocess.call([self.IPSEC, "start"]) + + def get_active_conns(self): + """This function parses output from 'ipsec status' command. + It returns dictionary where is interface name (as in OVSDB) + and is another dictionary. This another dictionary + uses strongSwan connection name as and more detailed + sample line from the parsed outpus as . """ + + conns = {} + proc = subprocess.Popen([self.IPSEC, 'status'], stdout=subprocess.PIPE) + + while True: + line = proc.stdout.readline().strip() + if line == '': + break + tunnel_name = line.split(":") + if len(tunnel_name) < 2: + continue + m = re.match(r"(.*)(-in-\d+|-out-\d+).*", tunnel_name[0]) + if not m: + continue + ifname = m.group(1) + if ifname not in conns: + conns[ifname] = {} + (conns[ifname])[tunnel_name[0]] = line + + return conns + + def config_init(self): + self.conf_file = open(self.IPSEC_CONF, "w") + self.secrets_file = open(self.IPSEC_SECRETS, "w") + self.conf_file.write(self.CONF_HEADER) + self.secrets_file.write(FILE_HEADER) + + def config_global(self, monitor): + """Configure the global state of IPsec tunnels.""" + needs_refresh = False + + if monitor.conf_in_use != monitor.conf: + monitor.conf_in_use = copy.deepcopy(monitor.conf) + needs_refresh = True + + # Configure the shunt policy + if monitor.conf_in_use["skb_mark"]: + skb_mark = monitor.conf_in_use["skb_mark"] + self.conf_file.write(SHUNT_POLICY.format(skb_mark)) + + # Configure the CA cert + if monitor.conf_in_use["pki"]["ca_cert"]: + cacert = monitor.conf_in_use["pki"]["ca_cert"] + self.conf_file.write(self.CA_SECTION % cacert) + + return needs_refresh + + def config_tunnel(self, tunnel): + if tunnel.conf["psk"]: + self.secrets_file.write('%s %s : PSK "%s"\n' % + (tunnel.conf["local_ip"], tunnel.conf["remote_ip"], + tunnel.conf["psk"])) + auth_section = self.auth_tmpl["psk"].substitute(tunnel.conf) + else: + self.secrets_file.write("%s %s : RSA %s\n" % + (tunnel.conf["local_ip"], tunnel.conf["remote_ip"], + tunnel.conf["private_key"])) + if tunnel.conf["remote_cert"]: + tmpl = self.auth_tmpl["pki_remote"] + auth_section = tmpl.substitute(tunnel.conf) + else: + tmpl = self.auth_tmpl["pki_ca"] + auth_section = tmpl.substitute(tunnel.conf) + + vals = tunnel.conf.copy() + vals["auth_section"] = auth_section + vals["version"] = tunnel.version + conf_text = transp_tmpl[tunnel.conf["tunnel_type"]].substitute(vals) + self.conf_file.write(conf_text) + + def config_fini(self): + self.secrets_file.close() + self.conf_file.close() + self.secrets_file = None + self.conf_file = None + + def refresh(self, monitor): + """This functions refreshes strongSwan configuration. Behind the + scenes this function calls: + 1. once "ipsec update" command that tells strongSwan to load + all new tunnels from "ipsec.conf"; and + 2. once "ipsec rereadsecrets" command that tells strongswan to load + secrets from "ipsec.conf" file + 3. for every removed tunnel "ipsec stroke down-nb " command + that removes old tunnels. + Once strongSwan vici bindings will be distributed with major + Linux distributions this function could be simplified.""" + vlog.info("Refreshing StrongSwan configuration") + subprocess.call([self.IPSEC, "update"]) + subprocess.call([self.IPSEC, "rereadsecrets"]) + # "ipsec update" command does not remove those tunnels that were + # updated or that disappeared from the ipsec.conf file. So, we have + # to manually remove them by calling "ipsec stroke down-nb " + # command. We use number to tell apart tunnels that + # were just updated. + # "ipsec down-nb" command is designed to be non-blocking (opposed + # to "ipsec down" command). This means that we should not be concerned + # about possibility of ovs-monitor-ipsec to block for each tunnel + # while strongSwan sends IKE messages over Internet. + conns_dict = self.get_active_conns() + for ifname, conns in conns_dict.iteritems(): + tunnel = monitor.tunnels.get(ifname) + for conn in conns: + # IPsec "connection" names that we choose in strongswan + # must start with Interface name + if not conn.startswith(ifname): + vlog.err("%s does not start with %s" % (conn, ifname)) + continue + + # version number should be the first integer after + # interface name in IPsec "connection" + try: + ver = int(re.findall(r'\d+', conn[len(ifname):])[0]) + except IndexError: + vlog.err("%s does not contain version number") + continue + except ValueError: + vlog.err("%s does not contain version number") + continue + + if not tunnel or tunnel.version != ver: + vlog.info("%s is outdated %u" % (conn, ver)) + subprocess.call([self.IPSEC, "stroke", "down-nb", conn]) + + +class LibreSwanHelper(object): + """This class does LibreSwan specific configurations.""" + CONF_HEADER = """%s +config setup + uniqueids=yes + +conn %%default + keyingtries=%%forever + type=transport + auto=route + ike=aes_gcm256-sha2_256 + esp=aes_gcm256 + ikev2=insist + +""" % (FILE_HEADER) + + auth_tmpl = {"psk": Template("""\ + left=$local_ip + right=$remote_ip + authby=secret"""), + "pki_remote": Template("""\ + left=$local_ip + right=$remote_ip + leftid=@$local_name + rightid=@$remote_name + leftcert="$local_name" + rightcert="$remote_name" + leftrsasigkey=%cert"""), + "pki_ca": Template("""\ + left=$local_ip + right=$remote_ip + leftid=@$local_name + rightid=@$remote_name + leftcert="$local_name" + leftrsasigkey=%cert + rightca=%same""")} + + def __init__(self, libreswan_root_prefix): + self.IPSEC = libreswan_root_prefix + "/usr/sbin/ipsec" + self.IPSEC_CONF = libreswan_root_prefix + "/etc/ipsec.conf" + self.IPSEC_SECRETS = libreswan_root_prefix + "/etc/ipsec.secrets" + self.conf_file = None + self.secrets_file = None + + def start_ike_daemon(self): + """This function starts LibreSwan.""" + f = open(self.IPSEC_CONF, "w") + f.write(self.CONF_HEADER) + f.close() + + f = open(self.IPSEC_SECRETS, "w") + f.write(FILE_HEADER) + f.close() + + vlog.info("Starting LibreSwan") + subprocess.call([self.IPSEC, "start"]) + + def config_init(self): + self.conf_file = open(self.IPSEC_CONF, "w") + self.secrets_file = open(self.IPSEC_SECRETS, "w") + self.conf_file.write(self.CONF_HEADER) + self.secrets_file.write(FILE_HEADER) + + def config_global(self, monitor): + """Configure the global state of IPsec tunnels.""" + needs_refresh = False + + if monitor.conf_in_use["pki"] != monitor.conf["pki"]: + # Clear old state + if monitor.conf_in_use["pki"]["certificate"]: + self._delete_local_certs_and_key(monitor.conf_in_use["pki"]) + + # Load new state + if monitor.conf["pki"]["certificate"]: + self._import_local_certs_and_key(monitor.conf["pki"]) + + monitor.conf_in_use["pki"] = copy.deepcopy(monitor.conf["pki"]) + needs_refresh = True + + # Configure the shunt policy + if monitor.conf["skb_mark"]: + self.conf_file.write(SHUNT_POLICY.format(monitor.conf["skb_mark"])) + + if monitor.conf_in_use["skb_mark"] != monitor.conf["skb_mark"]: + monitor.conf_in_use["skb_mark"] = monitor.conf["skb_mark"] + needs_refresh = True + + return needs_refresh + + def config_tunnel(self, tunnel): + if tunnel.conf["psk"]: + self.secrets_file.write('%s %s : PSK "%s"\n' % + (tunnel.conf["local_ip"], tunnel.conf["remote_ip"], + tunnel.conf["psk"])) + auth_section = self.auth_tmpl["psk"].substitute(tunnel.conf) + elif tunnel.conf["remote_cert"]: + auth_section = self.auth_tmpl["pki_remote"].substitute(tunnel.conf) + self._import_remote_cert(tunnel.conf["remote_cert"], + tunnel.conf["remote_name"]) + else: + auth_section = self.auth_tmpl["pki_ca"].substitute(tunnel.conf) + + vals = tunnel.conf.copy() + vals["auth_section"] = auth_section + vals["version"] = tunnel.version + conf_text = transp_tmpl[tunnel.conf["tunnel_type"]].substitute(vals) + self.conf_file.write(conf_text) + + def config_fini(self): + self.secrets_file.close() + self.conf_file.close() + self.secrets_file = None + self.conf_file = None + + def clear_tunnel_state(self, tunnel): + if tunnel.conf["remote_cert"]: + self._delete_remote_cert(tunnel.conf["remote_name"]) + + def refresh(self, monitor): + vlog.info("Refreshing LibreSwan configuration") + subprocess.call([self.IPSEC, "auto", "--rereadsecrets"]) + tunnels = set(monitor.tunnels.keys()) + + # Delete old connections + conns_dict = self.get_active_conns() + for ifname, conns in conns_dict.iteritems(): + tunnel = monitor.tunnels.get(ifname) + + for conn in conns: + # IPsec "connection" names must start with Interface name + if not conn.startswith(ifname): + vlog.err("%s does not start with %s" % (conn, ifname)) + continue + + # version number should be the first integer after + # interface name in IPsec "connection" + try: + ver = int(re.findall(r'\d+', conn[len(ifname):])[0]) + except ValueError: + vlog.err("%s does not contain version number") + continue + except IndexError: + vlog.err("%s does not contain version number") + continue + + if not tunnel or tunnel.version != ver: + vlog.info("%s is outdated %u" % (conn, ver)) + subprocess.call([self.IPSEC, "auto", "--delete", conn]) + elif ifname in tunnels: + tunnels.remove(ifname) + + # Activate new connections + for name in tunnels: + ver = monitor.tunnels[name].version + conn_in = "%s-in-%s" % (name, ver) + conn_out = "%s-out-%s" % (name, ver) + + # In a corner case, LibreSwan daemon restarts for some reason and + # the "ipsec auto --start" command is lost. Just retry to make sure + # the command is received by LibreSwan. + while True: + proc = subprocess.Popen([self.IPSEC, "auto", "--start", + "--asynchronous", conn_in], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + perr = str(proc.stderr.read()) + pout = str(proc.stdout.read()) + if not re.match(r".*Connection refused.*", perr) and \ + not re.match(r".*need --listen.*", pout): + break + + while True: + proc = subprocess.Popen([self.IPSEC, "auto", "--start", + "--asynchronous", conn_out], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + perr = str(proc.stderr.read()) + pout = str(proc.stdout.read()) + if not re.match(r".*Connection refused.*", perr) and \ + not re.match(r".*need --listen.*", pout): + break + + # Activate shunt policy if configured + if monitor.conf["skb_mark"]: + subprocess.call([self.IPSEC, "auto", "--start", + "--asynchronous", "prevent_unencrypted_gre"]) + subprocess.call([self.IPSEC, "auto", "--start", + "--asynchronous", "prevent_unencrypted_geneve"]) + subprocess.call([self.IPSEC, "auto", "--start", + "--asynchronous", "prevent_unencrypted_stt"]) + subprocess.call([self.IPSEC, "auto", "--start", + "--asynchronous", "prevent_unencrypted_vxlan"]) + + def get_active_conns(self): + """This function parses output from 'ipsec status' command. + It returns dictionary where is interface name (as in OVSDB) + and is another dictionary. This another dictionary + uses LibreSwan connection name as and more detailed + sample line from the parsed outpus as . """ + + conns = {} + proc = subprocess.Popen([self.IPSEC, 'status'], stdout=subprocess.PIPE) + + while True: + line = proc.stdout.readline().strip() + if line == '': + break + + m = re.search(r"#\d+: \"(.*)\".*", line) + if not m: + continue + + conn = m.group(1) + m = re.match(r"(.*)(-in-\d+|-out-\d+)", conn) + if not m: + continue + + ifname = m.group(1) + if ifname not in conns: + conns[ifname] = {} + (conns[ifname])[conn] = line + + return conns + + def _delete_remote_cert(self, remote_name): + """Delete remote certiticate from the NSS database.""" + try: + proc = subprocess.Popen(['certutil', '-D', '-d', + 'sql:/etc/ipsec.d/', '-n', remote_name], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + proc.wait() + if proc.returncode: + raise Exception(proc.stderr.read()) + except Exception as e: + vlog.err("Delete remote certificate failed.\n" + str(e)) + + def _import_remote_cert(self, cert, remote_name): + """Import remote certificate to the NSS database.""" + try: + proc = subprocess.Popen(['certutil', '-A', '-a', '-i', cert, + '-d', 'sql:/etc/ipsec.d/', '-n', + remote_name, '-t', 'P,P,P'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + proc.wait() + if proc.returncode: + raise Exception(proc.stderr.read()) + except Exception as e: + vlog.err("Import remote certificate failed.\n" + str(e)) + + def _delete_local_certs_and_key(self, pki): + """Delete certs and key from the NSS database.""" + name = pki["local_name"] + cacert = pki["ca_cert"] + + try: + # Delete certificate and private key + proc = subprocess.Popen(['certutil', '-F', '-d', + 'sql:/etc/ipsec.d/', '-n', name], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + proc.wait() + if proc.returncode: + raise Exception(proc.stderr.read()) + + if cacert: + proc = subprocess.Popen(['certutil', '-D', '-d', + 'sql:/etc/ipsec.d/', '-n', 'cacert'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + proc.wait() + if proc.returncode: + raise Exception(proc.stderr.read()) + except Exception as e: + vlog.err("Delete certs and key failed.\n" + str(e)) + + def _import_local_certs_and_key(self, pki): + """Import certs and key to the NSS database.""" + cert = pki["certificate"] + key = pki["private_key"] + name = pki["local_name"] + cacert = pki["ca_cert"] + + try: + # Create p12 file from pem files + proc = subprocess.Popen(['openssl', 'pkcs12', '-export', + '-in', cert, '-inkey', key, '-out', + '/tmp/%s.p12' % name, '-name', name, + '-passout', 'pass:'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + proc.wait() + if proc.returncode: + raise Exception(proc.stderr.read()) + + # Load p12 file to the database + proc = subprocess.Popen(['pk12util', '-i', '/tmp/%s.p12' % name, + '-d', 'sql:/etc/ipsec.d/', '-W', ''], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + proc.wait() + if proc.returncode: + raise Exception(proc.stderr.read()) + + if cacert: + proc = subprocess.Popen(['certutil', '-A', '-a', '-i', + cacert, '-d', 'sql:/etc/ipsec.d/', + '-n', 'cacert', '-t', 'CT,,'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + proc.wait() + if proc.returncode: + raise Exception(proc.stderr.read()) + except Exception as e: + vlog.err("Import cert and key failed.\n" + str(e)) + subprocess.call(['rm', '-f', '/tmp/%s.p12' % name]) + + +class IPsecTunnel(object): + """This is the base class for IPsec tunnel.""" + + unixctl_config_tmpl = Template("""\ + Tunnel Type: $tunnel_type + Local IP: $local_ip + Remote IP: $remote_ip + SKB mark: $skb_mark + Local cert: $certificate + Local name: $local_name + Local key: $private_key + Remote cert: $remote_cert + Remote name: $remote_name + CA cert: $ca_cert + PSK: $psk +""") + + unixctl_status_tmpl = Template("""\ + Ofport: $ofport + CFM state: $cfm_state +""") + + def __init__(self, name, row): + self.name = name # 'name' will not change because it is key in OVSDB + self.version = 0 # 'version' is increased on configuration changes + self.last_refreshed_version = -1 + self.state = "INIT" + self.conf = {} + self.status = {} + self.update_conf(row) + + def update_conf(self, row): + """This function updates IPsec tunnel configuration by using 'row' + from OVSDB interface table. If configuration was actually changed + in OVSDB then this function returns True. Otherwise, it returns + False.""" + ret = False + options = row.options + remote_cert = options.get("remote_cert") + remote_name = options.get("remote_name") + if remote_cert: + remote_name = monitor._get_cn_from_cert(remote_cert) + + new_conf = { + "ifname": self.name, + "tunnel_type": row.type, + "remote_ip": options.get("remote_ip"), + "local_ip": options.get("local_ip"), + "skb_mark": monitor.conf["skb_mark"], + "certificate": monitor.conf["pki"]["certificate"], + "private_key": monitor.conf["pki"]["private_key"], + "ca_cert": monitor.conf["pki"]["ca_cert"], + "remote_cert": remote_cert, + "remote_name": remote_name, + "local_name": monitor.conf["pki"]["local_name"], + "psk": options.get("psk")} + + if self.conf != new_conf: + # Configuration was updated in OVSDB. Validate it and figure + # out what to do next with this IPsec tunnel. Also, increment + # version number of this IPsec tunnel so that we could tell + # apart old and new tunnels in "ipsec status" output. + self.version += 1 + ret = True + self.conf = new_conf + + if self._is_valid_tunnel_conf(): + self.state = "CONFIGURED" + else: + vlog.warn("%s contains invalid configuration%s" % + (self.name, self.invalid_reason)) + self.state = "INVALID" + + new_status = { + "cfm_state": "Up" if row.cfm_fault == [False] else + "Down" if row.cfm_fault == [True] else + "Disabled", + "ofport": "Not assigned" if (row.ofport in [[], [-1]]) else + row.ofport[0]} + + if self.status != new_status: + # Tunnel has become unhealthy or ofport changed. Simply log this. + vlog.dbg("%s changed status from %s to %s" % + (self.name, str(self.status), str(new_status))) + self.status = new_status + return ret + + def mark_for_removal(self): + """This function marks tunnel for removal.""" + self.version += 1 + self.state = "REMOVED" + + def show(self, policies, securities, conns): + state = self.state + if self.state == "INVALID": + state += self.invalid_reason + header = "Interface name: %s v%u (%s)\n" % (self.name, self.version, + state) + conf = self.unixctl_config_tmpl.substitute(self.conf) + status = self.unixctl_status_tmpl.substitute(self.status) + spds = "Kernel policies installed:\n" + remote_ip = self.conf["remote_ip"] + if remote_ip in policies: + for line in policies[remote_ip]: + spds += " " + line + "\n" + sas = "Kernel security associations installed:\n" + if remote_ip in securities: + for line in securities[remote_ip]: + sas += " " + line + "\n" + cons = "IPsec connections that are active:\n" + if self.name in conns: + for tname in conns[self.name]: + cons += " " + conns[self.name][tname] + "\n" + + return header + conf + status + spds + sas + cons + "\n" + + def _is_valid_tunnel_conf(self): + """This function verifies if IPsec tunnel has valid configuration + set in 'conf'. If it is valid, then it returns True. Otherwise, + it returns False and sets the reason why configuration was considered + as invalid. + + This function could be improved in future to also verify validness + of certificates themselves so that ovs-monitor-ipsec would not + pass malformed configuration to IKE daemon.""" + + self.invalid_reason = None + + if not self.conf["remote_ip"]: + self.invalid_reason = ": 'remote_ip' is not set" + return False + + if not self.conf["local_ip"]: + self.invalid_reason = ": 'local_ip' is not set" + return False + + if self.conf["psk"]: + if self.conf["certificate"] or self.conf["private_key"] \ + or self.conf["ca_cert"] or self.conf["remote_cert"] \ + or self.conf["remote_name"]: + self.invalid_reason = ": 'certificate', 'private_key', "\ + "'ca_cert', 'remote_cert', and "\ + "'remote_name' must be unset with PSK" + return False + elif self.conf["remote_name"]: + if not self.conf["certificate"]: + self.invalid_reason = ": must set 'certificate' with PKI" + return False + elif not self.conf["private_key"]: + self.invalid_reason = ": must set 'private_key' with PKI" + return False + if not self.conf["remote_cert"] and not self.conf["ca_cert"]: + self.invalid_reason = ": must set 'remote_cert' or 'ca_cert'" + return False + else: + self.invalid_reason = ": must choose a authentication method" + return False + + return True + + +class IPsecMonitor(object): + """This class monitors and configures IPsec tunnels""" + + def __init__(self, root_prefix): + self.IPSEC = root_prefix + "/usr/sbin/ipsec" + self.tunnels = {} + + # Global configuration shared by all tunnels + self.conf = { + "pki": { + "private_key": None, + "certificate": None, + "ca_cert": None, + "local_name": None + }, + "skb_mark": None + } + self.conf_in_use = copy.deepcopy(self.conf) + + # Choose to either use StrongSwan or LibreSwan as IKE daemon + try: + proc = subprocess.Popen([self.IPSEC, "--version"], + stdout=subprocess.PIPE) + line = proc.stdout.readline().strip().split(" ") + + if len(line) >= 2 and line[1] in ["strongSwan", "Libreswan"]: + if line[1] == "strongSwan": + self.ike_helper = StrongSwanHelper(root_prefix) + else: + self.ike_helper = LibreSwanHelper(root_prefix) + else: + raise Exception("IKE daemon is not installed. Please install " + "the supported daemon (StrongSwan or LibreSwan).") + except Exception as e: + vlog.err(str(e)) + sys.exit(1) + + self.ike_helper.start_ike_daemon() + + def is_tunneling_type_supported(self, tunnel_type): + """Returns True if we know how to configure IPsec for these + types of tunnels. Otherwise, returns False.""" + return tunnel_type in ["gre", "geneve", "vxlan", "stt"] + + def is_ipsec_required(self, options_column): + """Return True if tunnel needs to be encrypted. Otherwise, + returns False.""" + return "psk" in options_column or \ + "remote_name" in options_column or \ + "remote_cert" in options_column + + def add_tunnel(self, name, row): + """Adds a new tunnel that monitor will provision with 'name'.""" + vlog.info("Tunnel %s appeared in OVSDB" % (name)) + self.tunnels[name] = IPsecTunnel(name, row) + + def update_tunnel(self, name, row): + """Updates configuration of already existing tunnel with 'name'.""" + tunnel = self.tunnels[name] + if tunnel.update_conf(row): + vlog.info("Tunnel's '%s' configuration changed in OVSDB to %u" % + (tunnel.name, tunnel.version)) + + def del_tunnel(self, name): + """Deletes tunnel by 'name'.""" + vlog.info("Tunnel %s disappeared from OVSDB" % (name)) + self.tunnels[name].mark_for_removal() + + def update_conf(self, pki, skb_mark): + """Update the global configuration for IPsec tunnels""" + # Update PKI certs and key + self.conf["pki"]["certificate"] = pki[0] + self.conf["pki"]["private_key"] = pki[1] + self.conf["pki"]["ca_cert"] = pki[2] + self.conf["pki"]["local_name"] = pki[3] + + # Update skb_mark used in IPsec policies. + self.conf["skb_mark"] = skb_mark + + def read_ovsdb_open_vswitch_table(self, data): + """This functions reads IPsec relevant configuration from Open_vSwitch + table.""" + pki = [None, None, None, None] + skb_mark = None + is_valid = False + + for row in data["Open_vSwitch"].rows.itervalues(): + pki[0] = row.other_config.get("certificate") + pki[1] = row.other_config.get("private_key") + pki[2] = row.other_config.get("ca_cert") + skb_mark = row.other_config.get("ipsec_skb_mark") + + # Test whether it's a valid configration + if pki[0] and pki[1]: + pki[3] = self._get_cn_from_cert(pki[0]) + if pki[3]: + is_valid = True + elif not pki[0] and not pki[1] and not pki[2]: + is_valid = True + + if not is_valid: + vlog.warn("The cert and key configuration is not valid. " + "The valid configuations are 1): certificate, private_key " + "and ca_cert are not set; or 2): certificate and " + "private_key are all set.") + else: + self.update_conf(pki, skb_mark) + + def read_ovsdb_interface_table(self, data): + """This function reads the IPsec relevant configuration from Interface + table.""" + ifaces = set() + + for row in data["Interface"].rows.itervalues(): + if not self.is_tunneling_type_supported(row.type): + continue + if not self.is_ipsec_required(row.options): + continue + if row.name in self.tunnels: + self.update_tunnel(row.name, row) + else: + self.add_tunnel(row.name, row) + ifaces.add(row.name) + + # Mark for removal those tunnels that just disappeared from OVSDB + for tunnel in self.tunnels.keys(): + if tunnel not in ifaces: + self.del_tunnel(tunnel) + + def read_ovsdb(self, data): + """This function reads all configuration from OVSDB that + ovs-monitor-ipsec is interested in.""" + self.read_ovsdb_open_vswitch_table(data) + self.read_ovsdb_interface_table(data) + + def show(self, unix_conn, policies, securities): + """This function prints all tunnel state in 'unix_conn'. + It uses 'policies' and securities' received from Linux Kernel + to show if tunnels were actually configured by the IKE deamon.""" + if not self.tunnels: + unix_conn.reply("No tunnels configured with IPsec") + return + s = "" + conns = self.ike_helper.get_active_conns() + for name, tunnel in self.tunnels.iteritems(): + s += tunnel.show(policies, securities, conns) + unix_conn.reply(s) + + def run(self): + """This function runs state machine that represents whole + IPsec configuration (i.e. merged together from individual + tunnel state machines). It creates configuration files and + tells IKE daemon to update configuration.""" + needs_refresh = False + removed_tunnels = [] + + self.ike_helper.config_init() + + if self.ike_helper.config_global(self): + needs_refresh = True + + for name, tunnel in self.tunnels.iteritems(): + if tunnel.last_refreshed_version != tunnel.version: + tunnel.last_refreshed_version = tunnel.version + needs_refresh = True + + if tunnel.state == "REMOVED" or tunnel.state == "INVALID": + removed_tunnels.append(name) + elif tunnel.state == "CONFIGURED": + self.ike_helper.config_tunnel(self.tunnels[name]) + + self.ike_helper.config_fini() + + for name in removed_tunnels: + # LibreSwan needs to clear state from database + if hasattr(self.ike_helper, "clear_tunnel_state"): + self.ike_helper.clear_tunnel_state(self.tunnels[name]) + del self.tunnels[name] + + if needs_refresh: + self.ike_helper.refresh(self) + + def _get_cn_from_cert(self, cert): + try: + proc = subprocess.Popen(['openssl', 'x509', '-noout', '-subject', + '-nameopt', 'RFC2253', '-in', cert], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + proc.wait() + if proc.returncode: + raise Exception(proc.stderr.read()) + m = re.search(r"CN=(.+?),", proc.stdout.readline()) + if not m: + raise Exception("No CN in the certificate subject.") + except Exception as e: + vlog.warn(str(e)) + return None + + return m.group(1) + + +def unixctl_xfrm_policies(conn, unused_argv, unused_aux): + global xfrm + policies = xfrm.get_policies() + conn.reply(str(policies)) + + +def unixctl_xfrm_state(conn, unused_argv, unused_aux): + global xfrm + securities = xfrm.get_securities() + conn.reply(str(securities)) + + +def unixctl_ipsec_status(conn, unused_argv, unused_aux): + global monitor + conns = monitor.ike_helper.get_active_conns() + conn.reply(str(conns)) + + +def unixctl_show(conn, unused_argv, unused_aux): + global monitor + global xfrm + policies = xfrm.get_policies() + securities = xfrm.get_securities() + monitor.show(conn, policies, securities) + + +def unixctl_refresh(conn, unused_argv, unused_aux): + global monitor + monitor.ike_helper.refresh(monitor) + conn.reply(None) + + +def unixctl_exit(conn, unused_argv, unused_aux): + global monitor + global exiting + exiting = True + + # Make sure persistent global states are cleared + monitor.update_conf([None, None, None, None], None) + # Make sure persistent tunnel states are cleared + for tunnel in monitor.tunnels.keys(): + monitor.del_tunnel(tunnel) + monitor.run() + + conn.reply(None) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("database", metavar="DATABASE", + help="A socket on which ovsdb-server is listening.") + parser.add_argument("--root-prefix", metavar="DIR", + help="Use DIR as alternate root directory" + " (for testing).") + + ovs.vlog.add_args(parser) + ovs.daemon.add_args(parser) + args = parser.parse_args() + ovs.vlog.handle_args(args) + ovs.daemon.handle_args(args) + + global monitor + global xfrm + + root_prefix = args.root_prefix if args.root_prefix else "" + xfrm = XFRM(root_prefix) + monitor = IPsecMonitor(root_prefix) + + remote = args.database + schema_helper = ovs.db.idl.SchemaHelper() + schema_helper.register_columns("Interface", + ["name", "type", "options", "cfm_fault", + "ofport"]) + schema_helper.register_columns("Open_vSwitch", ["other_config"]) + idl = ovs.db.idl.Idl(remote, schema_helper) + + ovs.daemon.daemonize() + + ovs.unixctl.command_register("xfrm/policies", "", 0, 0, + unixctl_xfrm_policies, None) + ovs.unixctl.command_register("xfrm/state", "", 0, 0, + unixctl_xfrm_state, None) + ovs.unixctl.command_register("ipsec/status", "", 0, 0, + unixctl_ipsec_status, None) + ovs.unixctl.command_register("tunnels/show", "", 0, 0, + unixctl_show, None) + ovs.unixctl.command_register("refresh", "", 0, 0, unixctl_refresh, None) + ovs.unixctl.command_register("exit", "", 0, 0, unixctl_exit, None) + + error, unixctl_server = ovs.unixctl.server.UnixctlServer.create(None) + if error: + ovs.util.ovs_fatal(error, "could not create unixctl server", vlog) + + # Sequence number when OVSDB was processed last time + seqno = idl.change_seqno + + while True: + unixctl_server.run() + if exiting: + break + + idl.run() + if seqno != idl.change_seqno: + monitor.read_ovsdb(idl.tables) + seqno = idl.change_seqno + + monitor.run() + + poller = ovs.poller.Poller() + unixctl_server.wait(poller) + idl.wait(poller) + poller.block() + + unixctl_server.close() + idl.close() + + +if __name__ == '__main__': + try: + main() + except SystemExit: + # Let system.exit() calls complete normally + raise + except: + vlog.exception("traceback") + sys.exit(ovs.daemon.RESTART_EXIT_CODE) From patchwork Mon Aug 6 18:04:35 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Qiuyu Xiao X-Patchwork-Id: 954129 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="VXP9wYGp"; dkim-atps=neutral Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 41klx95nLMz9s0R for ; Tue, 7 Aug 2018 04:07:41 +1000 (AEST) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 9D80ECF7; Mon, 6 Aug 2018 18:05:18 +0000 (UTC) X-Original-To: ovs-dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id 53A38CE9 for ; Mon, 6 Aug 2018 18:05:16 +0000 (UTC) X-Greylist: whitelisted by SQLgrey-1.7.6 Received: from mail-pl0-f51.google.com (mail-pl0-f51.google.com [209.85.160.51]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id 0FFE77C6 for ; Mon, 6 Aug 2018 18:05:13 +0000 (UTC) Received: by mail-pl0-f51.google.com with SMTP id t17-v6so5957520ply.13 for ; Mon, 06 Aug 2018 11:05:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=nM3VzU/dPAMonDGWg+5jzZUqAFKZ/CKNzuM6g20kP5k=; b=VXP9wYGpwafh8DYmy0AV5zFDCWedizL31l+hFq9BN0KgUA4fotkZpFwIkzmaLrtEDo /iFQGpt6Wlli3aFbjiGW4SauNxS6G+zmETQqSFWEWDRKN8DrnjoH7A4gKiV+c5KcvyoZ TYh2nRk+2yfqZfbT/Ed8H2qAhzYSc6ePg+waVJuCLGfNp5Ep01BwQdwiyCtPK/nF0RU4 R0xjN4CiRF/9gkzFhGSo7wI45U/mGSdjTQZhlahkmCz6OqFNadBD/KPvf999JpkP63yA Ivrj8nNS16+6frvUXM4Mwzgr92WnRmGueCgpn+3Txn7kNzsgKVq743EqpXzg6ZorFGc4 JYEQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=nM3VzU/dPAMonDGWg+5jzZUqAFKZ/CKNzuM6g20kP5k=; b=pKxbsyIDiprXQfcWWXZQIJy1jIeIfDErSp8ynKIxWRbWWn9UB2CJjnVnN2MZfw7xAV 3eIvrml0iPWven8adDN3wZjw8yPohdUcefE9hurkSUFaCGPMyah85LYiH0R6cdtu+LWI mBwMZmiMMZQKm1AJ5mk59HtAhI8bsUVgfLze9G2Y83PEHbtFoXFessnfKyaoKI9NMd3q 9e0KC3Gg+mcDk0MzAy50T7364/orf1h4gnQyANKiGpCrEP3gn4Fmo11DkXGpxYRoaxos vZZKP00sAuBl0hMUzhc7792MQgqnfArziMFCL0QtzOkLR71eVURMbgfU1pMgrskvo17V muhQ== X-Gm-Message-State: AOUpUlHIFXNuKx3Jt55oIQIpyoXfTAas1elky7sVBweAWqgYlpTUqFsw /gFIBiDvKK9nY7durBm/d3Qh8SeC X-Google-Smtp-Source: AAOMgpcquvQjAQjepJomm8OtpWghvZ0ycyWsfeTtbjI35M2DL10QV4x0P8TkymrfusqnY6u/tVTi/A== X-Received: by 2002:a17:902:7b96:: with SMTP id w22-v6mr14870402pll.24.1533578713282; Mon, 06 Aug 2018 11:05:13 -0700 (PDT) Received: from vm1.eng.vmware.com ([66.170.99.1]) by smtp.gmail.com with ESMTPSA id p64-v6sm23453965pfa.47.2018.08.06.11.05.12 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 06 Aug 2018 11:05:12 -0700 (PDT) From: Qiuyu Xiao To: ovs-dev@openvswitch.org Date: Mon, 6 Aug 2018 11:04:35 -0700 Message-Id: <20180806180439.16559-6-qiuyu.xiao.qyx@gmail.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20180806180439.16559-1-qiuyu.xiao.qyx@gmail.com> References: <20180806180439.16559-1-qiuyu.xiao.qyx@gmail.com> X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, FREEMAIL_FROM, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Cc: Ansis Atteka Subject: [ovs-dev] [PATCH v5 5/9] debian and rhel: Create IPsec package. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org Added rules and files to create debian and rpm ovs-ipsec packages. Signed-off-by: Qiuyu Xiao Signed-off-by: Ansis Atteka Co-authored-by: Ansis Atteka --- debian/automake.mk | 3 + debian/control | 21 ++ debian/openvswitch-ipsec.dirs | 1 + debian/openvswitch-ipsec.init | 181 ++++++++++++++++++ debian/openvswitch-ipsec.install | 1 + rhel/automake.mk | 1 + rhel/openvswitch-fedora.spec.in | 19 +- ...b_systemd_system_openvswitch-ipsec.service | 12 ++ utilities/ovs-ctl.in | 18 ++ 9 files changed, 256 insertions(+), 1 deletion(-) create mode 100644 debian/openvswitch-ipsec.dirs create mode 100644 debian/openvswitch-ipsec.init create mode 100644 debian/openvswitch-ipsec.install create mode 100644 rhel/usr_lib_systemd_system_openvswitch-ipsec.service diff --git a/debian/automake.mk b/debian/automake.mk index 4d8e204bb..8a8d43c9f 100644 --- a/debian/automake.mk +++ b/debian/automake.mk @@ -20,6 +20,9 @@ EXTRA_DIST += \ debian/openvswitch-datapath-source.copyright \ debian/openvswitch-datapath-source.dirs \ debian/openvswitch-datapath-source.install \ + debian/openvswitch-ipsec.dirs \ + debian/openvswitch-ipsec.init \ + debian/openvswitch-ipsec.install \ debian/openvswitch-pki.dirs \ debian/openvswitch-pki.postinst \ debian/openvswitch-pki.postrm \ diff --git a/debian/control b/debian/control index 9ae248f27..cde93f20e 100644 --- a/debian/control +++ b/debian/control @@ -322,3 +322,24 @@ Description: Open vSwitch development package 1000V. . This package provides openvswitch headers and libopenvswitch for developers. + +Package: openvswitch-ipsec +Architecture: linux-any +Depends: iproute2, + openvswitch-common (= ${binary:Version}), + openvswitch-switch (= ${binary:Version}), + python, + python-openvswitch (= ${source:Version}), + strongswan, + ${misc:Depends}, + ${shlibs:Depends} +Description: Open vSwitch IPsec tunneling support + Open vSwitch is a production quality, multilayer, software-based, + Ethernet virtual switch. It is designed to enable massive network + automation through programmatic extension, while still supporting + standard management interfaces and protocols (e.g. NetFlow, IPFIX, + sFlow, SPAN, RSPAN, CLI, LACP, 802.1ag). In addition, it is designed + to support distribution across multiple physical servers similar to + VMware's vNetwork distributed vswitch or Cisco's Nexus 1000V. + . + This package provides IPsec tunneling support for OVS tunnels. diff --git a/debian/openvswitch-ipsec.dirs b/debian/openvswitch-ipsec.dirs new file mode 100644 index 000000000..fca44aa7b --- /dev/null +++ b/debian/openvswitch-ipsec.dirs @@ -0,0 +1 @@ +usr/share/openvswitch/scripts \ No newline at end of file diff --git a/debian/openvswitch-ipsec.init b/debian/openvswitch-ipsec.init new file mode 100644 index 000000000..8488beccf --- /dev/null +++ b/debian/openvswitch-ipsec.init @@ -0,0 +1,181 @@ +#!/bin/sh +# +# Copyright (c) 2007, 2009 Javier Fernandez-Sanguino +# +# This is free software; you may redistribute it and/or modify +# it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2, +# or (at your option) any later version. +# +# This is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License with +# the Debian operating system, in /usr/share/common-licenses/GPL; if +# not, write to the Free Software Foundation, Inc., 59 Temple Place, +# Suite 330, Boston, MA 02111-1307 USA +# +### BEGIN INIT INFO +# Provides: openvswitch-ipsec +# Required-Start: $network $local_fs $remote_fs openvswitch-switch +# Required-Stop: $remote_fs +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Open vSwitch GRE-over-IPsec daemon +# Description: The ovs-monitor-ipsec script provides support for +# encrypting GRE tunnels with IPsec. +### END INIT INFO + +PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin + +DAEMON=/usr/share/openvswitch/scripts/ovs-monitor-ipsec # Daemon's location +NAME=ovs-monitor-ipsec # Introduce the short server's name here +LOGDIR=/var/log/openvswitch # Log directory to use +DATADIR=/usr/share/openvswitch + +PIDFILE=/var/run/openvswitch/$NAME.pid + +test -x $DAEMON || exit 0 + +. /lib/lsb/init-functions + +DODTIME=10 # Time to wait for the server to die, in seconds + # If this value is set too low you might not + # let some servers to die gracefully and + # 'restart' will not work + +set -e + +running_pid() { +# Check if a given process pid's cmdline matches a given name + pid=$1 + name=$2 + [ -z "$pid" ] && return 1 + [ ! -d /proc/$pid ] && return 1 + cmd=`cat /proc/$pid/cmdline | tr "\000" " "|cut -d " " -f 2` + # Is this the expected server + [ "$cmd" != "$name" ] && return 1 + return 0 +} + +running() { +# Check if the process is running looking at /proc +# (works for all users) + + # No pidfile, probably no daemon present + [ ! -f "$PIDFILE" ] && return 1 + pid=`cat $PIDFILE` + running_pid $pid $DAEMON || return 1 + return 0 +} + +start_server() { + ${DATADIR}/scripts/ovs-ctl start-ovs-ipsec + return 0 +} + +stop_server() { + ${DATADIR}/scripts/ovs-ctl stop-ovs-ipsec + return 0 +} + +force_stop() { +# Force the process to die killing it manually + [ ! -e "$PIDFILE" ] && return + if running ; then + kill -15 $pid + # Is it really dead? + sleep "$DODTIME" + if running ; then + kill -9 $pid + sleep "$DODTIME" + if running ; then + echo "Cannot kill $NAME (pid=$pid)!" + exit 1 + fi + fi + fi + rm -f $PIDFILE +} + + +case "$1" in + start) + log_daemon_msg "Starting $NAME" + # Check if it's running first + if running ; then + log_progress_msg "apparently already running" + log_end_msg 0 + exit 0 + fi + if start_server && running ; then + # It's ok, the server started and is running + log_end_msg 0 + else + # Either we could not start it or it is not running + # after we did + # NOTE: Some servers might die some time after they start, + # this code does not try to detect this and might give + # a false positive (use 'status' for that) + log_end_msg 1 + fi + ;; + stop) + log_daemon_msg "Stopping $NAME" + if running ; then + # Only stop the server if we see it running + stop_server + log_end_msg $? + else + # If it's not running don't do anything + log_progress_msg "apparently not running" + log_end_msg 0 + exit 0 + fi + ;; + force-stop) + # First try to stop gracefully the program + $0 stop + if running; then + # If it's still running try to kill it more forcefully + log_daemon_msg "Stopping (force) $NAME" + force_stop + log_end_msg $? + fi + ;; + restart|force-reload) + log_daemon_msg "Restarting $NAME" + stop_server + # Wait some sensible amount, some server need this + [ -n "$DODTIME" ] && sleep $DODTIME + start_server + running + log_end_msg $? + ;; + status) + log_daemon_msg "Checking status of $NAME" + if running ; then + log_progress_msg "running" + log_end_msg 0 + else + log_progress_msg "apparently not running" + log_end_msg 1 + exit 1 + fi + ;; + # Use this if the daemon cannot reload + reload) + log_warning_msg "Reloading $NAME daemon: not implemented, as the" + log_warning_msg "deamon cannot re-read the config file (use restart)." + ;; + *) + N=/etc/init.d/openvswitch-ipsec + echo "Usage: $N {start|stop|force-stop|restart|force-reload|status}" \ + >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/debian/openvswitch-ipsec.install b/debian/openvswitch-ipsec.install new file mode 100644 index 000000000..8fe665cb3 --- /dev/null +++ b/debian/openvswitch-ipsec.install @@ -0,0 +1 @@ +ipsec/ovs-monitor-ipsec usr/share/openvswitch/scripts diff --git a/rhel/automake.mk b/rhel/automake.mk index 7b6c78fd7..bc65d83e5 100644 --- a/rhel/automake.mk +++ b/rhel/automake.mk @@ -35,6 +35,7 @@ EXTRA_DIST += \ rhel/usr_lib_systemd_system_ovn-controller.service \ rhel/usr_lib_systemd_system_ovn-controller-vtep.service \ rhel/usr_lib_systemd_system_ovn-northd.service \ + rhel/usr_lib_systemd_system_openvswitch-ipsec.service \ rhel/usr_lib_firewalld_services_ovn-central-firewall-service.xml \ rhel/usr_lib_firewalld_services_ovn-host-firewall-service.xml diff --git a/rhel/openvswitch-fedora.spec.in b/rhel/openvswitch-fedora.spec.in index 8c18d39c2..dacc3ee56 100644 --- a/rhel/openvswitch-fedora.spec.in +++ b/rhel/openvswitch-fedora.spec.in @@ -209,6 +209,14 @@ Requires: openvswitch openvswitch-ovn-common %{_py2}-openvswitch %description ovn-docker Docker network plugins for OVN. +%package openvswitch-ipsec +Summary: Open vSwitch IPsec tunneling support +License: ASL 2.0 +Requires: openvswitch %{_py2}-openvswitch libreswan + +%description openvswitch-ipsec +This package provides IPsec tunneling support for OVS tunnels. + %prep %setup -q @@ -260,7 +268,8 @@ install -p -D -m 0644 \ rhel/usr_share_openvswitch_scripts_systemd_sysconfig.template \ $RPM_BUILD_ROOT/%{_sysconfdir}/sysconfig/openvswitch for service in openvswitch ovsdb-server ovs-vswitchd ovs-delete-transient-ports \ - ovn-controller ovn-controller-vtep ovn-northd; do + ovn-controller ovn-controller-vtep ovn-northd \ + openvswitch-ipsec; do install -p -D -m 0644 \ rhel/usr_lib_systemd_system_${service}.service \ $RPM_BUILD_ROOT%{_unitdir}/${service}.service @@ -318,6 +327,10 @@ install -p -D -m 0755 \ rhel/usr_share_openvswitch_scripts_ovs-systemd-reload \ $RPM_BUILD_ROOT%{_datadir}/openvswitch/scripts/ovs-systemd-reload +install -m 0755 \ + ipsec/ovs-monitor-ipsec \ + $RPM_BUILD_ROOT%{_datadir}/openvswitch/scripts/ovs-monitor-ipsec + # remove unpackaged files rm -f $RPM_BUILD_ROOT%{_bindir}/ovs-parse-backtrace \ $RPM_BUILD_ROOT%{_sbindir}/ovs-vlan-bug-workaround \ @@ -646,6 +659,10 @@ fi %{_mandir}/man8/ovn-controller-vtep.8* %{_unitdir}/ovn-controller-vtep.service +%files openvswitch-ipsec +%{_datadir}/openvswitch/scripts/ovs-monitor-ipsec +%{_unitdir}/openvswitch-ipsec.service + %changelog * Wed Jan 12 2011 Ralf Spenneberg - First build on F14 diff --git a/rhel/usr_lib_systemd_system_openvswitch-ipsec.service b/rhel/usr_lib_systemd_system_openvswitch-ipsec.service new file mode 100644 index 000000000..813844e51 --- /dev/null +++ b/rhel/usr_lib_systemd_system_openvswitch-ipsec.service @@ -0,0 +1,12 @@ +[Unit] +Description=OVS IPsec daemon +Requires=openvswitch.service +After=openvswitch.service + +[Service] +Type=forking +ExecStart=/usr/share/openvswitch/scripts/ovs-ctl start-ovs-ipsec +ExecStop=/usr/share/openvswitch/scripts/ovs-ctl stop-ovs-ipsec + +[Install] +WantedBy=multi-user.target diff --git a/utilities/ovs-ctl.in b/utilities/ovs-ctl.in index 43c8f32b7..d9b6ed943 100755 --- a/utilities/ovs-ctl.in +++ b/utilities/ovs-ctl.in @@ -222,6 +222,13 @@ start_forwarding () { return 0 } +start_ovs_ipsec () { + ${datadir}/scripts/ovs-monitor-ipsec \ + --pidfile=${rundir}/ovs-monitor-ipsec.pid \ + --log-file --detach --monitor unix:${rundir}/db.sock + return 0 +} + ## ---- ## ## stop ## ## ---- ## @@ -238,6 +245,11 @@ stop_forwarding () { fi } +stop_ovs_ipsec () { + ${bindir}/ovs-appctl -t ovs-monitor-ipsec exit + return 0 +} + ## --------------- ## ## enable-protocol ## ## --------------- ## @@ -522,6 +534,12 @@ case $command in delete-transient-ports) del_transient_ports ;; + start-ovs-ipsec) + start_ovs_ipsec + ;; + stop-ovs-ipsec) + stop_ovs_ipsec + ;; help) usage ;; From patchwork Mon Aug 6 18:04:36 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Qiuyu Xiao X-Patchwork-Id: 954138 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="WjIB87u8"; dkim-atps=neutral Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 41klyW5G84z9rvt for ; Tue, 7 Aug 2018 04:08:51 +1000 (AEST) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 3DE3FD16; Mon, 6 Aug 2018 18:05:26 +0000 (UTC) X-Original-To: ovs-dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id 8A7C2D09 for ; Mon, 6 Aug 2018 18:05:24 +0000 (UTC) X-Greylist: whitelisted by SQLgrey-1.7.6 Received: from mail-pl0-f66.google.com (mail-pl0-f66.google.com [209.85.160.66]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id 272187C1 for ; Mon, 6 Aug 2018 18:05:16 +0000 (UTC) Received: by mail-pl0-f66.google.com with SMTP id ba4-v6so5969143plb.11 for ; Mon, 06 Aug 2018 11:05:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=q3AebFJGoSY4psIccxaguUhjwCLUdK2wRJ2PUTEs7PQ=; b=WjIB87u8USm3VZpi6Jf4urOZgnhSuZ15g2yM6V1Yf9ELcAjMAJtUHmISB23f3aK6JD IcbhCo4+uUAFGtzRAluZnNktdFeWblgJEBoMK/uHJ4uXxUDaaqjicxhKGKit9a+oqSxt 19R/dHNNbF672FyeFxe8EbzNvFORvRqbFpTwmDsywNZOBwR+mWKGCxmtBuHSQmYenQWD XDor7M3Svk/KmqxRiUH295J+V6wZqajyXPV83IlnohZtFhKV52ksbflwvu4Vr8UAZ5id jjYJpAOpfM35SQ+s85/eQDC6M0GA/lI98BIHVmAYgYsyucCKWxSXUkiqvaWOM8+ehK+Q JBLw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=q3AebFJGoSY4psIccxaguUhjwCLUdK2wRJ2PUTEs7PQ=; b=CPlY801dMeXA0XUokXoJAyE+lADQ9XagoNh74bLGQiHA/HX1cagRnaQ6sMCg8B8ZTj Qfq2MLe3ghGRFIUqSB0qJ4c9dRR9rTjtO3QgeqGbpgONUgAsD+Xzq52+Ss03IUFEnjdi dIE9OWzWECSYytE/nVQ5vZ+R1ZJpoAzn2tXZRtarNmDRumFSL/IUmJyZw+/XrjKqp+lA eRvJy16kUpCQ1yHV/p6lfQxnK2u3ArPq5e4Siu9Ndu+OeL5SO4KiyNdI1jG6hRcHA5Ad VsHmJD2BkEQEASEepfXueLdwHTUrENEa0DfLoQXEpRftVVlC5tTM25rXpFs1zOFNJMOa gHLg== X-Gm-Message-State: AOUpUlGkQl8kGGWAcvkBS8U8qvyqrOA4bphFJNccMi9TwQINrF4eCWUS dCtMKadGiNcv8DZ3fIvHDskIIvzW X-Google-Smtp-Source: AAOMgpcL54OeKS7pc5SJL7q4imFZ+IJolMCp6vsItRt2/8pyNRUGPUtrt5/NTvWe4jVGmwxu0AebLg== X-Received: by 2002:a17:902:820a:: with SMTP id x10-v6mr14921814pln.261.1533578715070; Mon, 06 Aug 2018 11:05:15 -0700 (PDT) Received: from vm1.eng.vmware.com ([66.170.99.1]) by smtp.gmail.com with ESMTPSA id p64-v6sm23453965pfa.47.2018.08.06.11.05.14 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 06 Aug 2018 11:05:14 -0700 (PDT) From: Qiuyu Xiao To: ovs-dev@openvswitch.org Date: Mon, 6 Aug 2018 11:04:36 -0700 Message-Id: <20180806180439.16559-7-qiuyu.xiao.qyx@gmail.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20180806180439.16559-1-qiuyu.xiao.qyx@gmail.com> References: <20180806180439.16559-1-qiuyu.xiao.qyx@gmail.com> X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, FREEMAIL_FROM, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Cc: Ansis Atteka Subject: [ovs-dev] [PATCH v5 6/9] Documentation: IPsec tunnel tutorial and documentation. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org tutorials/index.rst gives a step-by-setp guide to set up OVS IPsec tunnel. tutorials/ipsec.rst gives detailed explanation on the IPsec tunnel configuration methods and forwarding modes. Signed-off-by: Qiuyu Xiao Signed-off-by: Ansis Atteka Co-authored-by: Ansis Atteka --- Documentation/automake.mk | 2 + Documentation/howto/index.rst | 1 + Documentation/howto/ipsec.rst | 200 +++++++++++++++++ Documentation/index.rst | 3 +- Documentation/tutorials/index.rst | 1 + Documentation/tutorials/ipsec.rst | 353 ++++++++++++++++++++++++++++++ vswitchd/vswitch.xml | 153 ++++++++++++- 7 files changed, 703 insertions(+), 10 deletions(-) create mode 100644 Documentation/howto/ipsec.rst create mode 100644 Documentation/tutorials/ipsec.rst diff --git a/Documentation/automake.mk b/Documentation/automake.mk index 244479490..5401b9bad 100644 --- a/Documentation/automake.mk +++ b/Documentation/automake.mk @@ -28,6 +28,7 @@ DOC_SOURCE = \ Documentation/tutorials/ovn-openstack.rst \ Documentation/tutorials/ovn-sandbox.rst \ Documentation/tutorials/ovs-conntrack.rst \ + Documentation/tutorials/ipsec.rst \ Documentation/topics/index.rst \ Documentation/topics/bonding.rst \ Documentation/topics/idl-compound-indexes.rst \ @@ -60,6 +61,7 @@ DOC_SOURCE = \ Documentation/howto/docker.rst \ Documentation/howto/dpdk.rst \ Documentation/howto/firewalld.rst \ + Documentation/howto/ipsec.rst \ Documentation/howto/kvm.rst \ Documentation/howto/libvirt.rst \ Documentation/howto/selinux.rst \ diff --git a/Documentation/howto/index.rst b/Documentation/howto/index.rst index 201d6936b..9a3487be3 100644 --- a/Documentation/howto/index.rst +++ b/Documentation/howto/index.rst @@ -37,6 +37,7 @@ OVS :maxdepth: 2 kvm + ipsec selinux libvirt ssl diff --git a/Documentation/howto/ipsec.rst b/Documentation/howto/ipsec.rst new file mode 100644 index 000000000..32e55b5ac --- /dev/null +++ b/Documentation/howto/ipsec.rst @@ -0,0 +1,200 @@ +.. + Licensed under the Apache License, Version 2.0 (the "License"); you may + not use this file except in compliance with the License. You may obtain + a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + License for the specific language governing permissions and limitations + under the License. + + Convention for heading levels in Open vSwitch documentation: + + ======= Heading 0 (reserved for the title in a document) + ------- Heading 1 + ~~~~~~~ Heading 2 + +++++++ Heading 3 + ''''''' Heading 4 + + Avoid deeper levels because they do not render well. + +======================================= +Encrypt Open vSwitch Tunnels with IPsec +======================================= + +This document gives detailed description on the OVS IPsec tunnel and its +configuration modes. If you want to follow a step-by-step guide to run and +test IPsec tunnel, please refer to :doc:`/tutorials/ipsec`. + +Overview +-------- + +Why do encryption? +~~~~~~~~~~~~~~~~~~ + +OVS tunnel packets are transported from one machine to another. Along the path, +the packets are processed by physical routers and physical switches. There are +risks that these physical devices might read or write the contents of the +tunnel packets. IPsec encrypts IP payload and prevents the malicious party +sniffing or manipulating the tunnel traffic. + +OVS IPsec +~~~~~~~~~ + +OVS IPsec aims to provide a simple interface for user to add encryption on OVS +tunnels. It supports GRE, GENEVE, VXLAN, and STT tunnel. The IPsec +configuration is done by setting options of the tunnel interface and +other_config of Open_vSwitch. You can choose different authentication methods +and forwarding modes based on your requirements. + +OVS does not currently provide any support for IPsec encryption for traffic not +encapsulated in a tunnel. + +Configuration +------------- + +Authentication Methods +~~~~~~~~~~~~~~~~~~~~~~ + +Hosts of the IPsec tunnel need to authenticate each other to build a secure +channel. There are three authentication methods: + +1) You can use a pre-shared key (PSK) to do authentication. In both hosts, set + the same PSK value. This PSK is like your password. You should never reveal + it to untrusted parties. This method is easier to use but less secure than + the certificate-based methods:: + + $ ovs-vsctl add-port br0 ipsec_gre0 -- \ + set interface ipsec_gre0 type=gre \ + options:local_ip=1.1.1.1 \ + options:remote_ip=2.2.2.2 \ + options:psk=swordfish + + .. note:: + + The ``local_ip`` field is required for the IPsec tunnel. + +2) You can use a self-signed certificate to do authentication. In each host, + generate a certificate and the paired private key. Copy the certificate of + the remote host to the local host and configure the OVS as following:: + + $ ovs-vsctl set Open_vSwitch . \ + other_config:certificate=/path/to/local_cert.pem \ + other_config:private_key=/path/to/priv_key.pem + $ ovs-vsctl add-port br0 ipsec_gre0 -- \ + set interface ipsec_gre0 type=gre \ + options:local_ip=1.1.1.1 \ + options:remote_ip=2.2.2.2 \ + options:remote_cert=/path/to/remote_cert.pem + + `local_cert.pem` is the certificate of the local host. `priv_key.pem` + is the private key of the local host. `priv_key.pem` needs to be stored in + a secure location. `remote_cert.pem` is the certificate of the remote host. + + .. note:: + + OVS IPsec requires x.509 version 3 certificate with the subjectAltName + DNS field setting the same string as the common name (CN) field. You can + follow the tutorial in :doc:`/tutorials/ipsec` and use ovs-pki(8) to + generate compatible certificate and key. + + (Before OVS version 2.10.90, ovs-pki(8) did not generate x.509 v3 + certificates, so if your existing PKI was generated by an older version, + it is not suitable for this purpose.) + +3) You can also use CA-signed certificate to do authentication. First, you need + to create a CA certificate and sign each host certificate with the CA key + (please see :doc:`/tutorials/ipsec`). Copy the CA certificate to each + host and configure the OVS as following:: + + $ ovs-vsctl set Open_vSwitch . \ + other_config:certificate=/path/to/local_cert.pem \ + other_config:private_key=/path/to/priv_key.pem \ + other_config:ca_cert=/path/to/ca_cert.pem + $ ovs-vsctl add-port br0 ipsec_gre0 -- \ + set interface ipsec_gre0 type=gre \ + options:local_ip=1.1.1.1 \ + options:remote_ip=2.2.2.2 \ + options:remote_name=remote_cn + + `ca_cert.pem` is the CA certificate. You need to set `remote_cn` as the + common name (CN) of the remote host's certificate so that only the + certificate with the expected CN can be trusted in this connection. It is + preferable to use this method than 2) if there are many remote hosts since + you don't have to copy every remote certificate to the local host. + + .. note:: + + When using certificate-based authentication, you should not set psk in + the interface options. When using psk-based authentication, you should + not set certificate, private_key, ca_cert, remote_cert, and remote_name. + +Forwarding Modes +~~~~~~~~~~~~~~~~ + +There is delay between the user setting up IPsec tunnel and the IPsec tunnel +actually taking affect to encrypt packets. To offset the risk of unencrypted +packets leaking out during this period, you can choose a more secure forwarding +mode. There are three forwarding modes: + +1) The default mode allows unencrypted packets to be sent before IPsec + completes negotiation:: + + $ ovs-vsctl add-port br0 ipsec_gre0 -- \ + set interface ipsec_gre0 type=gre \ + options:local_ip=1.1.1.1 \ + options:remote_ip=2.2.2.2 \ + options:psk=swordfish + + This mode should be used only and only if tunnel configuration is static + and/or if there is firewall that can drop the plain packets that + occasionally leak the tunnel unencrypted on OVSDB (re)configuration events. + +2) The ipsec_skb_mark mode drops unencrypted packets by using skb_mark of + tunnel packets:: + + $ ovs-vsctl set Open_vSwitch . other_config:ipsec_skb_mark=0/1 + $ ovs-vsctl add-port br0 ipsec_gre0 -- \ + set interface ipsec_gre0 type=gre \ + options:local_ip=1.1.1.1 \ + options:remote_ip=2.2.2.2 \ + options:psk=swordfish + + OVS IPsec drops unencrypted packets which carry the same skb_mark as + `ipsec_skb_mark`. By setting the ipsec_skb_mark as 0/1, OVS IPsec prevents + all unencrypted tunnel packets leaving the host since the default skb_mark + value for tunnel packets are 0. This affects all OVS tunnels including those + without IPsec being set up. You can install OpenFlow rules to whitelist + those non-IPsec tunnels by setting the skb_mark of the tunnel traffic as + non-zero value. + +3) Setting `ipsec_skb_mark` as 1/1 only drops tunnel packets with skb_mark + value being 1:: + + $ ovs-vsctl set Open_vSwitch . other_config:ipsec_skb_mark=1/1 + $ ovs-vsctl add-port br0 ipsec_gre0 -- \ + set interface ipsec_gre0 type=gre \ + options:local_ip=1.1.1.1 \ + options:remote_ip=2.2.2.2 \ + options:psk=swordfish + + Opposite to 2), this mode passes through unencrypted tunnel packets by + default. To drop unencrypted IPsec tunnel traffic, you need to explicitly + set skb_mark to a non-zero value for those tunnel traffic by installing + OpenFlow rules. + +Bug Reporting +------------- + +If you think you may have found a bug with security implications, like + +1) IPsec protected tunnel accepted packets that came unencrypted; OR +2) IPsec protected tunnel allowed packets to leave unencrypted + +then please report such bugs according to :doc:`/internals/security`. + +If the bug does not have security implications, then report it according to +instructions in :doc:`/internals/bugs`. diff --git a/Documentation/index.rst b/Documentation/index.rst index 06602f728..bab5ba1f1 100644 --- a/Documentation/index.rst +++ b/Documentation/index.rst @@ -65,7 +65,8 @@ vSwitch? Start here. :doc:`tutorials/ovs-advanced` | :doc:`tutorials/ovn-sandbox` | :doc:`tutorials/ovn-openstack` | - :doc:`tutorials/ovs-conntrack` + :doc:`tutorials/ovs-conntrack` | + :doc:`tutorials/ipsec` Deeper Dive ----------- diff --git a/Documentation/tutorials/index.rst b/Documentation/tutorials/index.rst index ab90b7c84..b481090a0 100644 --- a/Documentation/tutorials/index.rst +++ b/Documentation/tutorials/index.rst @@ -40,6 +40,7 @@ vSwitch. :maxdepth: 2 faucet + ipsec ovs-advanced ovn-sandbox ovn-openstack diff --git a/Documentation/tutorials/ipsec.rst b/Documentation/tutorials/ipsec.rst new file mode 100644 index 000000000..492ed223c --- /dev/null +++ b/Documentation/tutorials/ipsec.rst @@ -0,0 +1,353 @@ +.. + Licensed under the Apache License, Version 2.0 (the "License"); you may + not use this file except in compliance with the License. You may obtain + a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + License for the specific language governing permissions and limitations + under the License. + + Convention for heading levels in Open vSwitch documentation: + + ======= Heading 0 (reserved for the title in a document) + ------- Heading 1 + ~~~~~~~ Heading 2 + +++++++ Heading 3 + ''''''' Heading 4 + + Avoid deeper levels because they do not render well. + +================== +OVS IPsec Tutorial +================== + +This document provides a step-by-step guide for running IPsec tunnel in Open +vSwitch. A more detailed description on OVS IPsec tunnel and its +configuration modes can be found in :doc:`/howto/ipsec`. + +Requirements +------------ + +OVS IPsec tunnel requires Linux kernel (>= v3.10.0) and OVS out-of-tree kernel +module. The compatible IKE daemons are LibreSwan (>= v3.23) and StrongSwan +(>= v5.3.5). + +.. _install-ovs-ipsec: + +Installing OVS and IPsec Packages +--------------------------------- + +OVS IPsec has .deb and .rpm packages. You should use the right package +based on your Linux distribution. This tutorial uses Ubuntu 16.04 and Fedora 27 +as examples. + +Ubuntu +~~~~~~ + +1. Follow :doc:`/intro/install/debian` to build debian packages. + + .. note:: + + If you have already installed OVS, then you only need to install + openvswitch-pki_*.deb and openvswitch-ipsec_*.deb in the following step. + If your kernel version is below v4.13.0, update your kernel to v4.13.0 or + above. + +2. Install the related packages:: + + $ apt-get install dkms strongswan + $ dpkg -i libopenvswitch_*.deb openvswitch-common_*.deb \ + openvswitch-switch_*.deb openvswitch-datapath-dkms_*.deb \ + python-openvswitch_*.deb openvswitch-pki_*.deb \ + openvswitch-ipsec_*.deb + + If the installation is successful, you should be able to see the + ovs-monitor-ipsec daemon is running in your system. + +Fedora +~~~~~~ + +1. Follow :doc:`/intro/install/fedora` to build RPM packages. + +2. Install the related packages:: + + $ dnf install python2-openvswitch libreswan \ + "kernel-devel-uname-r == $(uname -r)" + $ rpm -i openvswitch-*.rpm openvswitch-kmod-*.rpm \ + openvswitch-openvswitch-ipsec-*.rpm + +3. Install firewall rules to allow ESP traffic:: + + $ iptables -A IN_FedoraServer_allow -p esp -j ACCEPT + +4. Run the openvswitch-ipsec service:: + + $ systemctl start openvswitch-ipsec.service + + .. note:: + + The SELinux policies might prevent openvswitch-ipsec.service to access + certain resources. You can configure SELinux to remove such restrictions. + +Configuring IPsec tunnel +------------------------ + +Suppose you want to build IPsec tunnel between two hosts. Assume `host_1`'s +external IP is 1.1.1.1, and `host_2`'s external IP is 2.2.2.2. Make sure +`host_1` and `host_2` can ping each other via these external IPs. + +0. Set up some variables to make life easier. On both hosts, set ``ip_1`` and + ``ip_2`` variables, e.g.:: + + $ ip_1=1.1.1.1 + $ ip_2=2.2.2.2 + +1. Set up OVS bridges in both hosts. + + In `host_1`:: + + $ ovs-vsctl add-br br-ipsec + $ ip addr add 192.0.0.1/24 dev br-ipsec + $ ip link set br-ipsec up + + In `host_2`:: + + $ ovs-vsctl add-br br-ipsec + $ ip addr add 192.0.0.2/24 dev br-ipsec + $ ip link set br-ipsec up + +2. Set up IPsec tunnel. + + There are three authentication methods. You can choose one to set up your + IPsec tunnel. + + a) Using pre-shared key: + + In `host_1`:: + + $ ovs-vsctl add-port br-ipsec tun -- \ + set interface tun type=gre \ + options:local_ip=$ip_1 \ + options:remote_ip=$ip_2 \ + options:psk=swordfish + + In `host_2`:: + + $ ovs-vsctl add-port br-ipsec tun -- \ + set interface tun type=gre \ + options:local_ip=$ip_2 \ + options:remote_ip=$ip_1 \ + options:psk=swordfish + + .. note:: + + Pre-shared key (PSK) based authentication is easy to set up but less + secure compared with other authentication methods. You should use it + cautiously in production systems. + + b) Using self-signed certificate: + + Generate self-signed certificate in both `host_1` and `host_2`. Then copy + the certificate of `host_1` to `host_2` and the certificate of `host_2` + to `host_1`. + + In `host_1`:: + + $ ovs-pki req -u host_1 + $ ovs-pki self-sign host_1 + $ scp host_1-cert.pem $ip_2:/etc/keys/host_1-cert.pem + + In `host_2`:: + + $ ovs-pki req -u host_2 + $ ovs-pki self-sign host_2 + $ scp host_2-cert.pem $ip_1:/etc/keys/host_2-cert.pem + + .. note:: + + If you use StrongSwan as IKE daemon, please move the host certificates + to /etc/ipsec.d/certs/ and private key to /etc/ipsec.d/private/ so that + StrongSwan has permission to access those files. + + Configure IPsec tunnel to use self-signed certificates. + + In `host_1`:: + + $ ovs-vsctl set Open_vSwitch . \ + other_config:certificate=/etc/keys/host_1-cert.pem \ + other_config:private_key=/etc/keys/host_1-privkey.pem + $ ovs-vsctl add-port br-ipsec tun -- \ + set interface tun type=gre \ + options:local_ip=$ip_1 \ + options:remote_ip=$ip_2 \ + options:remote_cert=/etc/keys/host_2-cert.pem + + In `host_2`:: + + $ ovs-vsctl set Open_vSwitch . \ + other_config:certificate=/etc/keys/host_2-cert.pem \ + other_config:private_key=/etc/keys/host_2-privkey.pem + $ ovs-vsctl add-port br-ipsec tun -- \ + set interface tun type=gre \ + options:local_ip=$ip_2 \ + options:remote_ip=$ip_1 \ + options:remote_cert=/etc/keys/host_1-cert.pem + + .. note:: + + The confidentiality of the private key is very critical. Don't copy it + to places where it might be compromised. (The certificate need not be + kept confidential.) + + c) Using CA-signed certificate: + + First you need to establish a public key infrastructure (PKI). Suppose + you choose `host_1` to host PKI. + + In `host_1`:: + + $ ovs-pki init + + Generate certificate requests and copy the certificate request of + `host_2` to `host_1`. + + In `host_1`:: + + $ ovs-pki req -u host_1 + + In `host_2`:: + + $ ovs-pki req -u host_2 + $ scp host_2-req.pem $ip_1:/etc/keys/host_2-req.pem + + Sign the certificate requests with the CA key. Copy `host_2`'s signed + certificate and the CA certificate to `host_2`. + + In `host_1`:: + + $ ovs-pki sign host_1 switch + $ ovs-pki sign host_2 switch + $ scp host_2-cert.pem $ip_2:/etc/keys/host_2-cert.pem + $ scp /var/lib/openvswitch/pki/switchca/cacert.pem \ + $ip_2:/etc/keys/cacert.pem + + .. note:: + + If you use StrongSwan as IKE daemon, please move the host certificates + to /etc/ipsec.d/certs/, CA certificate to /etc/ipsec.d/cacerts/, and + private key to /etc/ipsec.d/private/ so that StrongSwan has permission + to access those files. + + Configure IPsec tunnel to use CA-signed certificate. + + In `host_1`:: + + $ ovs-vsctl set Open_vSwitch . \ + other_config:certificate=/etc/keys/host_1-cert.pem \ + other_config:private_key=/etc/keys/host_1-privkey.pem \ + other_config:ca_cert=/etc/keys/cacert.pem + $ ovs-vsctl add-port br-ipsec tun -- \ + set interface tun type=gre \ + options:local_ip=$ip_1 \ + options:remote_ip=$ip_2 \ + options:remote_name=host_2 + + In `host_2`:: + + $ ovs-vsctl set Open_vSwitch . \ + other_config:certificate=/etc/keys/host_2-cert.pem \ + other_config:private_key=/etc/keys/host_2-privkey.pem \ + other_config:ca_cert=/etc/keys/cacert.pem + $ ovs-vsctl add-port br-ipsec tun -- \ + set interface tun type=gre \ + options:local_ip=$ip_2 \ + options:remote_ip=$ip_1 \ + options:remote_name=host_1 + + .. note:: + + remote_name is the common name (CN) of the signed-certificate. It must + match the name given as the argument to the ``ovs-pki sign command``. + It ensures that only certificate with the expected CN can be + authenticated; otherwise, any certificate signed by the CA would be + accepted. + +3. Test IPsec tunnel. + + Now you should have an IPsec GRE tunnel running between two hosts. To verify + it, in `host_1`:: + + $ ping 192.0.0.2 & + $ tcpdump -ni any net $ip_2 + + You should be able to see that ESP packets are being sent from `host_1` to + `host_2`. + +Troubleshooting +--------------- + +The ``ovs-monitor-ipsec`` daemon manages and monitors the IPsec tunnel state. +Use the following ``ovs-appctl`` command to view ``ovs-monitor-ipsec`` internal +representation of tunnel configuration:: + + $ ovs-appctl -t ovs-monitor-ipsec tunnels/show + +If there is misconfiguration, then ``ovs-appctl`` should indicate why. +For example:: + + Interface name: gre0 v5 (CONFIGURED) <--- Should be set to CONFIGURED. + Otherwise, error message will + be provided + Tunnel Type: gre + Local IP: 1.1.1.1 + Remote IP: 2.2.2.2 + SKB mark: None + Local cert: None + Local name: None + Local key: None + Remote cert: None + Remote name: None + CA cert: None + PSK: swordfish + Ofport: 1 <--- Whether ovs-vswitchd has assigned Ofport + number to this Tunnel Port + CFM state: Up <--- Whether CFM declared this tunnel healthy + Kernel policies installed: + ... <--- IPsec policies for this OVS tunnel in + Linux Kernel installed by strongSwan + Kernel security associations installed: + ... <--- IPsec security associations for this OVS + tunnel in Linux Kernel installed by + strongswan + IPsec connections that are active: + ... <--- IPsec "connections" for this OVS + tunnel + +If you don't see any active connections, try to run the following command to +refresh the ``ovs-monitor-ipsec`` daemon:: + + $ ovs-appctl -t ovs-monitor-ipsec refresh + +You can also check the logs of the ``ovs-monitor-ipsec`` daemon and the IKE +daemon to locate issues. ``ovs-monitor-ipsec`` outputs log messages to +/var/log/openvswitch/ovs-monitor-ipsec.log. + +Bug Reporting +------------- + +If you think you may have found a bug with security implications, like + +1. IPsec protected tunnel accepted packets that came unencrypted; OR +2. IPsec protected tunnel allowed packets to leave unencrypted; + +Then report such bugs according to :doc:`/internals/security`. + +If bug does not have security implications, then report it according to +instructions in :doc:`/internals/bugs`. + +If you have suggestions to improve this tutorial, please send a email to +ovs-discuss@openvswitch.org. diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml index 634294944..3caef4f79 100644 --- a/vswitchd/vswitch.xml +++ b/vswitchd/vswitch.xml @@ -503,10 +503,8 @@ OpenFlow specification mandates the timeout to be at least one second. The default is 10 seconds.

- - + - Sequence number for client to increment. When a client modifies @@ -770,6 +768,114 @@ + +

+ These settings control the global configuration of IPsec tunnels. The + options column of the Interface table + configures IPsec for individual tunnels. +

+

+ OVS IPsec supports the following three forms of authentication. + Currently, all IPsec tunnels must use the same form: +

+
    +
  1. + Pre-shared keys: Omit the global settings. On each tunnel, set . +
  2. +
  3. + Self-signed certificates: Set the private_key and + certificate global settings. On each tunnel, set . The remote certificate can be + self-signed. +
  4. +
  5. + CA-signed certificates: Set all of the global settings. On each + tunnel, set to the common + name (CN) of the remote certificate. The remote certificate must be + signed by the CA. +
  6. +
+ +

+ Name of a PEM file containing the private key used as the switch's + identity for IPsec tunnels. +

+
+ +

+ Name of a PEM file containing a certificate that certifies the + switch's private key, and identifies a trustworthy switch for IPsec + tunnels. The certificate must be x.509 version 3 and with the + string in common name (CN) also set in the subject alternative name + (SAN). +

+
+ +

+ Name of a PEM file containing the CA certificate used to verify + that a remote switch of the IPsec tunnel is trustworthy. +

+
+ + +

+ After an IPsec tunnel is configured, it takes a few round trips to + negotiate details of the encryption with the remote host. In the + meantime, packets sent by the local host over the tunnel can be + transmitted in plaintext. This setting controls the behavior in this + situation. +

+ +

+ This setting takes the form + value/mask. If it is specified, + then the skb_mark field in every outgoing tunneled + packet sent in plaintext is compared against it and, if it matches, + the packet is dropped. This is a global setting that is applied to + every tunneled packet, regardless of whether IPsec encryption is + enabled for the tunnel, the type of tunnel, or whether OVS is + involved. +

+ +

+ Example policies: +

+ +
+
1/1
+
+ Drop all unencrypted tunneled packets in which the + least-significant bit of skb_mark is 1. This would + be a useful policy given an OpenFlow flow table that sets + skb_mark to 1 for traffic that should be encrypted. + The default skb_mark is 0, so this would not affect + other traffic. +
+ +
0/1
+
+ Drop all unencrypted tunneled packets in which the + least-significant bit of skb_mark is 0. This would + be a useful policy if no unencrypted tunneled traffic should exit + the system without being specially whitelisted by setting + skb_mark to 1. +
+ +
(empty)
+
+ If this setting is empty or unset, then all unencrypted tunneled + packets are transmitted in the usual way. +
+
+
+
+
+ The overall purpose of these columns is described under Common Columns at the beginning of this document. @@ -1409,7 +1515,7 @@ - +

A port within a .

Most commonly, a port has exactly one ``interface,'' pointed to by its @@ -2398,9 +2504,9 @@

- Optional. The tunnel destination IP that received packets must - match. Default is to match all addresses. If specified, may be one - of: + Normally optional; required for IPsec tunnels. The tunnel + destination IP that received packets must match. Default is to match + all addresses. If specified, may be one of:

    @@ -2655,7 +2761,7 @@

    - Optional. Compute encapsulation header (either GRE or UDP) + Optional. Compute encapsulation header (either GRE or UDP) checksums on outgoing packets. Default is disabled, set to true to enable. Checksums present on incoming packets will be validated regardless of this setting. @@ -2673,8 +2779,37 @@ - + +

    + Setting any of these options enables IPsec support for a given + tunnel. gre, geneve, vxlan, + and stt interfaces support these options. See the + IPsec section in the table + for a description of each mode. +

    + +

    + In PSK mode only, the preshared secret to negotiate tunnel. This + value must match on both tunnel ends. +

    +
    + +

    + In self-signed certificate mode only, name of a PEM file + containing a certificate of the remote switch. The certificate + must be x.509 version 3 and with the string in common name (CN) + also set in the subject alternative name (SAN). +

    +
    + +

    + In CA-signed certificate mode only, common name (CN) of the remote + certificate. +

    +
    + +

    Only erspan interfaces support these options. From patchwork Mon Aug 6 18:04:37 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Qiuyu Xiao X-Patchwork-Id: 954132 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="aBJHkO1L"; dkim-atps=neutral Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 41klxd4Js5z9rvt for ; Tue, 7 Aug 2018 04:08:05 +1000 (AEST) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 69F7ACED; Mon, 6 Aug 2018 18:05:19 +0000 (UTC) X-Original-To: ovs-dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id 2E79BCE6 for ; Mon, 6 Aug 2018 18:05:18 +0000 (UTC) X-Greylist: whitelisted by SQLgrey-1.7.6 Received: from mail-pl0-f49.google.com (mail-pl0-f49.google.com [209.85.160.49]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id 38D037C9 for ; Mon, 6 Aug 2018 18:05:17 +0000 (UTC) Received: by mail-pl0-f49.google.com with SMTP id g6-v6so2410683plq.9 for ; Mon, 06 Aug 2018 11:05:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=ocPAXppxG4EmUIHxPCVORJHpou3IRhKM0mxYCMQG10o=; b=aBJHkO1LgaRE2MDAABMYUc96YkBnaPxfd9g/2Dh528kWMhBhEJ1ab0CqZm18jpO5c5 Fau1Nqva8NO+9LBMYQln2aQ9EZ4gewU274E4rzAeJkdon/5rHr6+QWVmji+N4xBQrjQT 89DV/LBbZcmlkFCcVd2qzNA9RCrc11QJj3Nitxw7AAZjmqZ3MbU/HMjQ5w3zl9kqjHdU slB6fyc6iig5Wb+SMbuMaDPYn0wDCqIqI2rdRnr21YWEz7CDrhMQweYDy/g6cXvbmc5L HGGhbPCW4znl+/2xdV2f/dibxUNcAbeSduUzFC2l28aZdZAllC5qxGIXFT17/LnCZjcV E5bA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=ocPAXppxG4EmUIHxPCVORJHpou3IRhKM0mxYCMQG10o=; b=QWFUzP0ycj2b3sQs/bWy+wvLhGKlI/ChebHtcI4+07jPddZxGE3oslsWWkWC6qKrIq MPIc5c1b0xJy70t+uIqhxJRBocJ4vHOlfCalnJp8Ts1v4wxzNnjABexqrMyHySCnJvdn HvzG+MKo26+pjAPSbERxVLaR/ys5hn+tne8PolaMXn3MAAWk6KnZEPpwWrCu0UWhkR+V fgVcfHQ0Z+uTFqdEile0cW045pCq3vkHSYnIaOQLs0k2ABcoqrF3SjYGhtgBjKFTnlV/ PS8yCKNt+0TbxqnK/3lf9mYzqLrGYcmTg/DeYR6ixWujHZGQQGxGHKf9OtdzDmVv6Erw byRw== X-Gm-Message-State: AOUpUlG4rsFwZXGutUOLixDwceMLjLlTH8CDD/aE0fdQkqbHaqGi1jle PapCnabSrHBRr4cnGLKR4eWUowKX X-Google-Smtp-Source: AAOMgpddKvDSmeqUdIHLH4pGYj0EQgb23sY+aOrNb88OmjssCC0Q4n2YtRUT4S/U+WrGnCUBd+6j8Q== X-Received: by 2002:a17:902:be07:: with SMTP id r7-v6mr15259849pls.124.1533578716615; Mon, 06 Aug 2018 11:05:16 -0700 (PDT) Received: from vm1.eng.vmware.com ([66.170.99.1]) by smtp.gmail.com with ESMTPSA id p64-v6sm23453965pfa.47.2018.08.06.11.05.15 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 06 Aug 2018 11:05:16 -0700 (PDT) From: Qiuyu Xiao To: ovs-dev@openvswitch.org Date: Mon, 6 Aug 2018 11:04:37 -0700 Message-Id: <20180806180439.16559-8-qiuyu.xiao.qyx@gmail.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20180806180439.16559-1-qiuyu.xiao.qyx@gmail.com> References: <20180806180439.16559-1-qiuyu.xiao.qyx@gmail.com> X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, FREEMAIL_FROM, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [PATCH v5 7/9] ovs-pki: generate x.509 v3 certificate X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org This patch modifies ovs-pki to generate x.509 version 3 certificate. Compared with the x.509 v1 certificate generated by ovs-pki, version 3 certificate adds subjectAltName field and sets its value the same as common name (CN). The main reason for this change is to enable strongSwan IKE daemon to extract certificate identity string from the subjectAltName field, which makes OVN IPsec implementation easier. Signed-off-by: Qiuyu Xiao --- NEWS | 3 +++ utilities/ovs-pki.in | 25 +++++++++++++++++++++---- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/NEWS b/NEWS index f05a6e976..27ef12d59 100644 --- a/NEWS +++ b/NEWS @@ -59,6 +59,9 @@ v2.10.0 - xx xxx xxxx both kernel datapath and userspace datapath. * Added port-based and flow-based ERSPAN tunnel port support, added OpenFlow rules matching ERSPAN fields. See ovs-fields(7). + - ovs-pki + * ovs-pki now generates x.509 version 3 certificate. The new format adds + subjectAltName field and sets its value the same as common name (CN). v2.9.0 - 19 Feb 2018 -------------------- diff --git a/utilities/ovs-pki.in b/utilities/ovs-pki.in index 4f6941865..e0ba910f9 100755 --- a/utilities/ovs-pki.in +++ b/utilities/ovs-pki.in @@ -284,7 +284,7 @@ policy = policy # default policy email_in_dn = no # Don't add the email into cert DN name_opt = ca_default # Subject name display option cert_opt = ca_default # Certificate display option -copy_extensions = none # Don't copy extensions from request +copy_extensions = copy # Copy extensions from request unique_subject = no # Allow certs with duplicate subjects # For the CA policy @@ -295,6 +295,13 @@ organizationName = match organizationalUnitName = optional commonName = supplied emailAddress = optional + +# For the x509v3 extension +[ ca_cert ] +basicConstraints=CA:true + +[ usr_cert ] +basicConstraints=CA:false EOF fi @@ -307,7 +314,8 @@ EOF openssl req -config ca.cnf -nodes \ -newkey $newkey -keyout private/cakey.pem -out careq.pem \ 1>&3 2>&3 - openssl ca -config ca.cnf -create_serial -out cacert.pem \ + openssl ca -config ca.cnf -create_serial \ + -extensions ca_cert -out cacert.pem \ -days 3650 -batch -keyfile private/cakey.pem -selfsign \ -infiles careq.pem 1>&3 2>&3 chmod 0700 private/cakey.pem @@ -445,6 +453,7 @@ make_request() { [ req ] prompt = no distinguished_name = req_distinguished_name +req_extensions = v3_req [ req_distinguished_name ] C = US @@ -453,6 +462,9 @@ L = Palo Alto O = Open vSwitch OU = Open vSwitch certifier CN = $cn + +[ v3_req ] +subjectAltName = DNS:$cn EOF if test $keytype = rsa; then (umask 077 && openssl genrsa -out "$1-privkey.pem" $bits) 1>&3 2>&3 \ @@ -481,7 +493,7 @@ sign_request() { esac (cd "$pkidir/${type}ca" && - openssl ca -config ca.cnf -batch -in "$request_file") \ + openssl ca -config ca.cnf -extensions usr_cert -batch -in "$request_file") \ > "$2.tmp$$" 2>&3 mv "$2.tmp$$" "$2" } @@ -529,11 +541,16 @@ elif test "$command" = self-sign; then must_exist "$arg1-req.pem" must_exist "$arg1-privkey.pem" must_not_exist "$arg1-cert.pem" + make_tmpdir + cat > "$TMP/v3.ext" <&3 || exit $? + -signkey "$arg1-privkey.pem" -req -days 3650 -text \ + -extfile $TMP/v3.ext) 2>&3 || exit $? # Reset the permissions on the certificate to the user's default. cat "$arg1-cert.pem.tmp" > "$arg1-cert.pem" From patchwork Mon Aug 6 18:04:38 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Qiuyu Xiao X-Patchwork-Id: 954141 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="bIXr9AJW"; dkim-atps=neutral Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 41klyx2dsFz9rxx for ; Tue, 7 Aug 2018 04:09:13 +1000 (AEST) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 17830D12; Mon, 6 Aug 2018 18:05:27 +0000 (UTC) X-Original-To: ovs-dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id D38E5D09 for ; Mon, 6 Aug 2018 18:05:24 +0000 (UTC) X-Greylist: whitelisted by SQLgrey-1.7.6 Received: from mail-pl0-f48.google.com (mail-pl0-f48.google.com [209.85.160.48]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id 3F7D37CF for ; Mon, 6 Aug 2018 18:05:19 +0000 (UTC) Received: by mail-pl0-f48.google.com with SMTP id s17-v6so5970989plp.7 for ; Mon, 06 Aug 2018 11:05:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=KigUF4/cmQU7Qd4W8rGfVHMT6glSl7q98o6/lgckZnU=; b=bIXr9AJWvlaXvLAz4LSdW21uAQ4PPPegAQqmOhbVKatAsuFL7C7f78L7ffKefnWCDm PXgJmZb9WTXPSy9cMC965QwIukJqJzGpfsaIdi8vuH1SNxB1/TVcl8zJNrtQIWwkDY4p oZggv9VH3r8Pr5lB4VxoWOiY19s0jhcbf3SAdPutBVWn8npkrpE6UIQp9QGMG45pHpyl 8IH0CBSWnnZbWe4UJ/G4woHD7nfO1x1wizdkBXGURAwQH+6MxSzq86zNpeS88CD48Qkh Ay2Pr2IcmIWXBjm7AolHM+7Xj6p3N4vgpzvxZXm3pIzSpcgD84HlMWzPFReCC1LuCuvU SkPA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=KigUF4/cmQU7Qd4W8rGfVHMT6glSl7q98o6/lgckZnU=; b=VQ/lNqQNElMZFq6+Xpm7dfTc5TXesmG2DloxyOfFkjdT008mcNuhlgtaJouh1mI1My Jx7ntIf2Y2T+U98BWZElz9fT0msk1cdjDuNSY5rdbNa5PZtNsx2/OI+V5R+KFLUdVe9m B1SvK7KyIUumeux6OYbOLFv15NIgJHttsi91keDtGhDSle+j8py22QOmLUh9ZRcjTnC3 fta5i5aLq+vngiDfKcol7ElRa5mvTDbXlyxP66nQM/gVr7JISR+ZFHNiXCZVkd3JMmrL piLXgQi3lJKEpM1gLLHB/b6Kp9iBRnSSxZAD3uAfhl9QVaobzDTDIWeVr/aYJKS+sJB4 nzYA== X-Gm-Message-State: AOUpUlETEHy2ld3FiSsMbHmYUGq8X2GZZnK8b7kGOAfVSbnNHIjO7fd9 3i1Q1q35UciSSpn4VjRREWRmNQbj X-Google-Smtp-Source: AAOMgpcpUzY8tBuUZ2blqQFcaZULUNWzHwQrGYXE10AZqYpxV5rTBtKyUWZMGKvDbhLNyu+bYMMV+A== X-Received: by 2002:a17:902:246a:: with SMTP id m39-v6mr14658299plg.57.1533578718523; Mon, 06 Aug 2018 11:05:18 -0700 (PDT) Received: from vm1.eng.vmware.com ([66.170.99.1]) by smtp.gmail.com with ESMTPSA id p64-v6sm23453965pfa.47.2018.08.06.11.05.17 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 06 Aug 2018 11:05:18 -0700 (PDT) From: Qiuyu Xiao To: ovs-dev@openvswitch.org Date: Mon, 6 Aug 2018 11:04:38 -0700 Message-Id: <20180806180439.16559-9-qiuyu.xiao.qyx@gmail.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20180806180439.16559-1-qiuyu.xiao.qyx@gmail.com> References: <20180806180439.16559-1-qiuyu.xiao.qyx@gmail.com> X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, FREEMAIL_FROM, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [PATCH v5 8/9] OVN: native support for tunnel encryption X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org This patch adds IPsec support for OVN tunnel. Basically, OVN offers a binary option to its user for encryption configuration. If the IPsec option is turned on, all tunnels will be encrypted. Otherwise, no tunnel will be encrypted. The changes are summarized as below: 1) Added a ipsec column on the NB_Global table and SB_Global table. The value of ipsec column is propagated by ovn-northd from NB_Global to SB_Global. 2) ovn-controller monitors the ipsec column in SB_Global. If the ipsec value is true, ovn-controller sets options of the tunnel interface by specifying "options:remote_name=". If the ipsec value is false, ovn-controller removes these options. 3) ovs-monitor-ipsec daemon (https://mail.openvswitch.org/pipermail/ovs-dev/2018-June/348701.html) monitors the tunnel interface options and configures IKE daemon accordingly for IPsec encryption. Signed-off-by: Qiuyu Xiao --- ovn/controller/encaps.c | 31 ++++++++++++++++++++++---- ovn/controller/encaps.h | 7 +++++- ovn/controller/ovn-controller.c | 4 +++- ovn/northd/ovn-northd.c | 8 +++++-- ovn/ovn-architecture.7.xml | 39 +++++++++++++++++++++++++++++++++ ovn/ovn-nb.ovsschema | 7 +++--- ovn/ovn-nb.xml | 6 +++++ ovn/ovn-sb.ovsschema | 7 +++--- ovn/ovn-sb.xml | 6 +++++ 9 files changed, 101 insertions(+), 14 deletions(-) diff --git a/ovn/controller/encaps.c b/ovn/controller/encaps.c index fde017586..2169920ba 100644 --- a/ovn/controller/encaps.c +++ b/ovn/controller/encaps.c @@ -79,8 +79,9 @@ tunnel_create_name(struct tunnel_ctx *tc, const char *chassis_id) } static void -tunnel_add(struct tunnel_ctx *tc, const char *new_chassis_id, - const struct sbrec_encap *encap) +tunnel_add(struct tunnel_ctx *tc, const struct sbrec_sb_global *sbg, + const char *new_chassis_id, const struct sbrec_encap *encap, + const char *local_ip) { struct smap options = SMAP_INITIALIZER(&options); smap_add(&options, "remote_ip", encap->ip); @@ -90,6 +91,16 @@ tunnel_add(struct tunnel_ctx *tc, const char *new_chassis_id, smap_add(&options, "csum", csum); } + /* Add auth info if ipsec is enabled. */ + if (sbg->ipsec) { + smap_add(&options, "remote_name", new_chassis_id); + if (local_ip) { + smap_add(&options, "local_ip", local_ip); + } else { + VLOG_INFO("Need to specify encap ip for IPsec tunnels."); + } + } + /* If there's an existing chassis record that does not need any change, * keep it. Otherwise, create a new record (if there was an existing * record, the new record will supplant it and encaps_run() will delete @@ -157,7 +168,9 @@ encaps_run(struct ovsdb_idl_txn *ovs_idl_txn, const struct ovsrec_bridge_table *bridge_table, const struct ovsrec_bridge *br_int, const struct sbrec_chassis_table *chassis_table, - const char *chassis_id) + const char *chassis_id, + const struct sbrec_sb_global *sbg, + const struct ovsrec_open_vswitch_table *ovs_table) { if (!ovs_idl_txn || !br_int) { return; @@ -201,6 +214,16 @@ encaps_run(struct ovsdb_idl_txn *ovs_idl_txn, } } + /* Get IP address of local chassis. */ + const char *chassis_ip; + const struct ovsrec_open_vswitch *cfg; + cfg = ovsrec_open_vswitch_table_first(ovs_table); + if (cfg) { + chassis_ip = smap_get(&cfg->external_ids, "ovn-encap-ip"); + } else { + chassis_ip = NULL; + } + SBREC_CHASSIS_TABLE_FOR_EACH (chassis_rec, chassis_table) { if (strcmp(chassis_rec->name, chassis_id)) { /* Create tunnels to the other chassis. */ @@ -209,7 +232,7 @@ encaps_run(struct ovsdb_idl_txn *ovs_idl_txn, VLOG_INFO("No supported encaps for '%s'", chassis_rec->name); continue; } - tunnel_add(&tc, chassis_rec->name, encap); + tunnel_add(&tc, sbg, chassis_rec->name, encap, chassis_ip); } } diff --git a/ovn/controller/encaps.h b/ovn/controller/encaps.h index 054bdfa78..680b62df7 100644 --- a/ovn/controller/encaps.h +++ b/ovn/controller/encaps.h @@ -23,13 +23,18 @@ struct ovsdb_idl_txn; struct ovsrec_bridge; struct ovsrec_bridge_table; struct sbrec_chassis_table; +struct sbrec_sb_global; +struct ovsrec_open_vswitch_table; void encaps_register_ovs_idl(struct ovsdb_idl *); void encaps_run(struct ovsdb_idl_txn *ovs_idl_txn, const struct ovsrec_bridge_table *, const struct ovsrec_bridge *br_int, const struct sbrec_chassis_table *, - const char *chassis_id); + const char *chassis_id, + const struct sbrec_sb_global *, + const struct ovsrec_open_vswitch_table *); + bool encaps_cleanup(struct ovsdb_idl_txn *ovs_idl_txn, const struct ovsrec_bridge *br_int); diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c index 81d330683..b25e51f78 100644 --- a/ovn/controller/ovn-controller.c +++ b/ovn/controller/ovn-controller.c @@ -689,7 +689,9 @@ main(int argc, char *argv[]) chassis_id, br_int); encaps_run(ovs_idl_txn, ovsrec_bridge_table_get(ovs_idl_loop.idl), br_int, - sbrec_chassis_table_get(ovnsb_idl_loop.idl), chassis_id); + sbrec_chassis_table_get(ovnsb_idl_loop.idl), chassis_id, + sbrec_sb_global_first(ovnsb_idl_loop.idl), + ovsrec_open_vswitch_table_get(ovs_idl_loop.idl)); bfd_calculate_active_tunnels(br_int, &active_tunnels); binding_run(ovnsb_idl_txn, ovs_idl_txn, sbrec_chassis_by_name, sbrec_datapath_binding_by_key, diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c index 35baabcad..cd327d246 100644 --- a/ovn/northd/ovn-northd.c +++ b/ovn/northd/ovn-northd.c @@ -6884,8 +6884,8 @@ ovnnb_db_run(struct northd_context *ctx, } hmap_destroy(&ports); - /* Copy nb_cfg from northbound to southbound database. - * + /* Sync ipsec configuration. + * Copy nb_cfg from northbound to southbound database. * Also set up to update sb_cfg once our southbound transaction commits. */ const struct nbrec_nb_global *nb = nbrec_nb_global_first(ctx->ovnnb_idl); if (!nb) { @@ -6895,6 +6895,9 @@ ovnnb_db_run(struct northd_context *ctx, if (!sb) { sb = sbrec_sb_global_insert(ctx->ovnsb_txn); } + if (nb->ipsec != sb->ipsec) { + sbrec_sb_global_set_ipsec(sb, nb->ipsec); + } sbrec_sb_global_set_nb_cfg(sb, nb->nb_cfg); sb_loop->next_cfg = nb->nb_cfg; @@ -7398,6 +7401,7 @@ main(int argc, char *argv[]) ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_sb_global); add_column_noalert(ovnsb_idl_loop.idl, &sbrec_sb_global_col_nb_cfg); + add_column_noalert(ovnsb_idl_loop.idl, &sbrec_sb_global_col_ipsec); ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_logical_flow); add_column_noalert(ovnsb_idl_loop.idl, diff --git a/ovn/ovn-architecture.7.xml b/ovn/ovn-architecture.7.xml index ae5ca8e4a..55a5db9ea 100644 --- a/ovn/ovn-architecture.7.xml +++ b/ovn/ovn-architecture.7.xml @@ -1621,6 +1621,45 @@ +

    Encrypt Tunnel Traffic with IPsec

    +

    + OVN tunnel traffic goes through physical routers and switches. These + physical devices could be untrusted (devices in public network) or might be + compromised. Enabling encryption to the tunnel traffic can prevent the + traffic data from being monitored and manipulated. +

    +

    + The tunnel traffic is encrypted with IPsec. The CMS sets the + ipsec column in the northbound NB_Global table to + enable or disable IPsec encrytion. If ipsec is true, all OVN + tunnels will be encrypted. If ipsec is false, no OVN tunnels + will be encrypted. +

    +

    + When CMS updates the ipsec column in the northbound + NB_Global table, ovn-northd copies the value to + the ipsec column in the southbound SB_Global + table. ovn-controller in each chassis monitors the southbound + database and sets the options of the OVS tunnel interface accordingly. OVS + tunnel interface options are monitored by the + ovs-monitor-ipsec daemon which configures IKE daemon to set up + IPsec connections. +

    +

    + Chassis authenticates each other by using certificate. The authentication + succeeds if the other end in tunnel presents a certificate signed by a + trusted CA and the common name (CN) matches the expected chassis name. The + SSL certificates used in role-based access controls (RBAC) can be used in + IPsec. Or use ovs-pki to create different certificates. The + certificate is required to be x.509 version 3, and with CN field and + subjectAltName field being set to the chassis name. +

    +

    + The CA certificate, chassis certificate and private key are required to be + installed in each chassis before enabling IPsec. Please see + ovs-vswitchd.conf.db(5) for setting up CA based IPsec + authentication. +

    Design Decisions

    Tunnel Encapsulations

    diff --git a/ovn/ovn-nb.ovsschema b/ovn/ovn-nb.ovsschema index 3e7164baa..3ee288935 100644 --- a/ovn/ovn-nb.ovsschema +++ b/ovn/ovn-nb.ovsschema @@ -1,7 +1,7 @@ { "name": "OVN_Northbound", - "version": "5.13.0", - "cksum": "1278623084 20312", + "version": "5.14.0", + "cksum": "483549684 20358", "tables": { "NB_Global": { "columns": { @@ -19,7 +19,8 @@ "ssl": { "type": {"key": {"type": "uuid", "refTable": "SSL"}, - "min": 0, "max": 1}}}, + "min": 0, "max": 1}}, + "ipsec": {"type": "boolean"}}, "maxRows": 1, "isRoot": true}, "Logical_Switch": { diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml index bc60a25dd..8c2d736d2 100644 --- a/ovn/ovn-nb.xml +++ b/ovn/ovn-nb.xml @@ -80,6 +80,12 @@ Global SSL configuration.
    + + + Tunnel encryption configuration. If this column is set to be true, all + OVN tunnels will be encrypted with IPsec. + +
diff --git a/ovn/ovn-sb.ovsschema b/ovn/ovn-sb.ovsschema index ad6ad3b71..669b034e1 100644 --- a/ovn/ovn-sb.ovsschema +++ b/ovn/ovn-sb.ovsschema @@ -1,7 +1,7 @@ { "name": "OVN_Southbound", - "version": "1.16.0", - "cksum": "3046632234 14844", + "version": "1.17.0", + "cksum": "3604753998 14890", "tables": { "SB_Global": { "columns": { @@ -17,7 +17,8 @@ "ssl": { "type": {"key": {"type": "uuid", "refTable": "SSL"}, - "min": 0, "max": 1}}}, + "min": 0, "max": 1}}, + "ipsec": {"type": "boolean"}}, "maxRows": 1, "isRoot": true}, "Chassis": { diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml index 68e31db31..6d4b662b9 100644 --- a/ovn/ovn-sb.xml +++ b/ovn/ovn-sb.xml @@ -174,6 +174,12 @@ Global SSL configuration. + + + Tunnel encryption configuration. If this column is set to be true, all + OVN tunnels will be encrypted with IPsec. + +
From patchwork Mon Aug 6 18:04:39 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Qiuyu Xiao X-Patchwork-Id: 954135 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="edZp1ZjJ"; dkim-atps=neutral Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 41kly12WQjz9rvt for ; Tue, 7 Aug 2018 04:08:25 +1000 (AEST) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 3683AD0D; Mon, 6 Aug 2018 18:05:24 +0000 (UTC) X-Original-To: ovs-dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id C985BCFD for ; Mon, 6 Aug 2018 18:05:22 +0000 (UTC) X-Greylist: whitelisted by SQLgrey-1.7.6 Received: from mail-pf1-f178.google.com (mail-pf1-f178.google.com [209.85.210.178]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id 1EDC97C7 for ; Mon, 6 Aug 2018 18:05:21 +0000 (UTC) Received: by mail-pf1-f178.google.com with SMTP id p12-v6so7245094pfh.2 for ; Mon, 06 Aug 2018 11:05:21 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=pGy8EH7Chk15AHrj0WMmIp/fhR9ftmjnJYPRWP7PdUc=; b=edZp1ZjJ1AWu/QT+6CtofXKcvhXQbuT/mpnuHLPqFFCTFSNgoU0di9/5hkRho5Oc8O qU9fRhSM8MBtvF6wZ5zXo//tI12g1/Rccf9jBVaB8gSwzaU3IecKNxUxGgrqsEXC7FWD MbxdK/mnypMaUAQVF/ZC5+lHAni6Uuae9Fvae+IHAy+4lbI92+006Wz7cSdEGLm/9sj4 GuNbKlcX+2PFHnaujvq0PKDOif6FeI3JcXCw61i/xx7//jCiCFfdVmGI7aGEJ6+scH8m NPaeK9eRm8YspVMt7YXGbg8BL9g3UvL7uDWjaJnx8tih92BIv1PpwvLMFbTW56HYgtgw V1qQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=pGy8EH7Chk15AHrj0WMmIp/fhR9ftmjnJYPRWP7PdUc=; b=pL1Dnd7azYyT1owW6SR9o8JEIlWHx1SEe+/obVw90pbjDYl4v7NHUaGyVL1OT2lkYA 7XVMN8lOf5grASS3gRalbhSIuinBaZjd/voHJDsJoj5KIgaYUfe2lzTCGeUSP5SL0V9R SjHCnCtqkByR/NQYimOrz7gEYZ9eAMdV9OaafusjSyTDDLZmTO54jd6AzAPQoAWm9n2G fbkqHKKGRxMeBeyHN7Iowizvh0uFWF1jmtO1ho5o9bW2G5Txi/SJOGkrWbcRRq1T99BF lMA1Wys4LTuzvtEHDCmfBsuBQKrzZ7fFmEICZYgj4CXIoFhLdQmZdKG1VslhBIB3YOOr ALfA== X-Gm-Message-State: AOUpUlGJN9Vc+zHVCC7h0L/0ItYi7A16+v9SKjA7HDnWbGlwGEyOZWeH SUkaQJKaYvtgPlwWkEBHhMK4OAlY X-Google-Smtp-Source: AAOMgpcpWmkgcBhd2eAk274Q+H8f5EfeMYAPtsob0Szfpiz/RMgNLEs3Dg5GX24zY+LL4j2+7qpmrw== X-Received: by 2002:a63:5922:: with SMTP id n34-v6mr15576555pgb.113.1533578720374; Mon, 06 Aug 2018 11:05:20 -0700 (PDT) Received: from vm1.eng.vmware.com ([66.170.99.1]) by smtp.gmail.com with ESMTPSA id p64-v6sm23453965pfa.47.2018.08.06.11.05.19 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 06 Aug 2018 11:05:19 -0700 (PDT) From: Qiuyu Xiao To: ovs-dev@openvswitch.org Date: Mon, 6 Aug 2018 11:04:39 -0700 Message-Id: <20180806180439.16559-10-qiuyu.xiao.qyx@gmail.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20180806180439.16559-1-qiuyu.xiao.qyx@gmail.com> References: <20180806180439.16559-1-qiuyu.xiao.qyx@gmail.com> X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, FREEMAIL_FROM, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [PATCH v5 9/9] Documentation: OVN RBAC and IPsec tutorial X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org This patch adds step-by-step guide for configuring OVN Role-Based Access Control and IPsec. Signed-off-by: Qiuyu Xiao --- Documentation/automake.mk | 2 + Documentation/index.rst | 4 +- Documentation/tutorials/index.rst | 2 + Documentation/tutorials/ovn-ipsec.rst | 146 ++++++++++++++++++++++++++ Documentation/tutorials/ovn-rbac.rst | 134 +++++++++++++++++++++++ 5 files changed, 287 insertions(+), 1 deletion(-) create mode 100644 Documentation/tutorials/ovn-ipsec.rst create mode 100644 Documentation/tutorials/ovn-rbac.rst diff --git a/Documentation/automake.mk b/Documentation/automake.mk index 5401b9bad..082438e09 100644 --- a/Documentation/automake.mk +++ b/Documentation/automake.mk @@ -29,6 +29,8 @@ DOC_SOURCE = \ Documentation/tutorials/ovn-sandbox.rst \ Documentation/tutorials/ovs-conntrack.rst \ Documentation/tutorials/ipsec.rst \ + Documentation/tutorials/ovn-ipsec.rst \ + Documentation/tutorials/ovn-rbac.rst \ Documentation/topics/index.rst \ Documentation/topics/bonding.rst \ Documentation/topics/idl-compound-indexes.rst \ diff --git a/Documentation/index.rst b/Documentation/index.rst index bab5ba1f1..46261235c 100644 --- a/Documentation/index.rst +++ b/Documentation/index.rst @@ -66,7 +66,9 @@ vSwitch? Start here. :doc:`tutorials/ovn-sandbox` | :doc:`tutorials/ovn-openstack` | :doc:`tutorials/ovs-conntrack` | - :doc:`tutorials/ipsec` + :doc:`tutorials/ipsec` | + :doc:`tutorials/ovn-ipsec` | + :doc:`tutorials/ovn-rbac` Deeper Dive ----------- diff --git a/Documentation/tutorials/index.rst b/Documentation/tutorials/index.rst index b481090a0..35340ee56 100644 --- a/Documentation/tutorials/index.rst +++ b/Documentation/tutorials/index.rst @@ -44,4 +44,6 @@ vSwitch. ovs-advanced ovn-sandbox ovn-openstack + ovn-rbac + ovn-ipsec ovs-conntrack diff --git a/Documentation/tutorials/ovn-ipsec.rst b/Documentation/tutorials/ovn-ipsec.rst new file mode 100644 index 000000000..5a8701905 --- /dev/null +++ b/Documentation/tutorials/ovn-ipsec.rst @@ -0,0 +1,146 @@ +.. + Licensed under the Apache License, Version 2.0 (the "License"); you may + not use this file except in compliance with the License. You may obtain + a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + License for the specific language governing permissions and limitations + under the License. + + Convention for heading levels in Open vSwitch documentation: + + ======= Heading 0 (reserved for the title in a document) + ------- Heading 1 + ~~~~~~~ Heading 2 + +++++++ Heading 3 + ''''''' Heading 4 + + Avoid deeper levels because they do not render well. + +================== +OVN IPsec Tutorial +================== + +This document provides a step-by-step guide for encrypting tunnel traffic with +IPsec in Open Virtual Network (OVN). OVN tunnel traffic is transported by +physical routers and switches. These physical devices could be untrusted +(devices in public network) or might be compromised. Enabling IPsec encryption +for the tunnel traffic can prevent the traffic data from being monitored and +manipulated. More details about the OVN IPsec design can be found in +``ovn-architecture``\(7) manpage. + +This document assumes OVN is installed in your system and runs normally. Also, +you need to install OVS IPsec packages in each chassis (refer to +:ref:`install-ovs-ipsec`). + +Generating Certificates and Keys +-------------------------------- + +OVN chassis uses CA-signed certificate to authenticate peer chassis for +building IPsec tunnel. If you have enabled Role-Based Access Control (RBAC) in +OVN, you can use the RBAC SSL certificates and keys to set up OVN IPsec. Or you +can generate separate certificates and keys with ``ovs-pki`` (refer to +:ref:`gen-certs-keys`). + +.. note:: + + OVN IPsec requires x.509 version 3 certificate with the subjectAltName DNS + field setting the same string as the common name (CN) field. CN should be + set as the chassis name. ``ovs-pki`` in Open vSwitch 2.10.90 and later + generates such certificates. Please generate compatible certificates if you + use another PKI tool, or an older version of ``ovs-pki``, to manage + certificates. + +Configuring OVN IPsec +--------------------- + +You need to install the CA certificate, chassis certificate and private key in +each chassis. Use the following command:: + + $ ovs-vsctl set Open_vSwitch . \ + other_config:certificate=/path/to/chassis-cert.pem \ + other_config:private_key=/path/to/chassis-privkey.pem \ + other_config:ca_cert=/path/to/cacert.pem + +Enabling OVN IPsec +------------------ + +To enable OVN IPsec, set ``ipsec`` column in ``NB_Global`` table of the +northbound database to true:: + + $ ovn-nbctl set nb_global . ipsec=true + +With OVN IPsec enabled, all tunnel traffic in OVN will be encrypted with IPsec. +To disable it, set ``ipsec`` column in ``NB_Global`` table of the northbound +database to false:: + + $ ovn-nbctl set nb_global . ipsec=false + +Troubleshooting +--------------- + +The ``ovs-monitor-ipsec`` daemon in each chassis manages and monitors the IPsec +tunnel state. Use the following ``ovs-appctl`` command to view +``ovs-monitor-ipsec`` internal representation of tunnel configuration:: + + $ ovs-appctl -t ovs-monitor-ipsec tunnels/show + +If there is a misconfiguration, then ``ovs-appctl`` should indicate why. +For example:: + + Interface name: ovn-host_2-0 v1 (CONFIGURED) <--- Should be set to CONFIGURED. + Otherwise, error message will + be provided + Tunnel Type: geneve + Local IP: 1.1.1.1 + Remote IP: 2.2.2.2 + SKB mark: None + Local cert: /path/to/chassis-cert.pem + Local name: host_1 + Local key: /path/to/chassis-privkey.pem + Remote cert: None + Remote name: host_2 + CA cert: /path/to/cacert.pem + PSK: None + Ofport: 2 <--- Whether ovs-vswitchd has assigned Ofport + number to this Tunnel Port + CFM state: Disabled <--- Whether CFM declared this tunnel healthy + Kernel policies installed: + ... <--- IPsec policies for this OVS tunnel in + Linux Kernel installed by strongSwan + Kernel security associations installed: + ... <--- IPsec security associations for this OVS + tunnel in Linux Kernel installed by + strongswan + IPsec connections that are active: + ... <--- IPsec "connections" for this OVS + tunnel + +If you don't see any active connections, try to run the following command to +refresh the ``ovs-monitor-ipsec`` daemon:: + + $ ovs-appctl -t ovs-monitor-ipsec refresh + +You can also check the logs of the ``ovs-monitor-ipsec`` daemon and the IKE +daemon to locate issues. ``ovs-monitor-ipsec`` outputs log messages to +``/var/log/openvswitch/ovs-monitor-ipsec.log``. + +Bug Reporting +------------- + +If you think you may have found a bug with security implications, like + +1. IPsec protected tunnel accepted packets that came unencrypted; OR +2. IPsec protected tunnel allowed packets to leave unencrypted; + +Then report such bugs according to :doc:`/internals/security`. + +If bug does not have security implications, then report it according to +instructions in :doc:`/internals/bugs`. + +If you have suggestions to improve this tutorial, please send a email to +ovs-discuss@openvswitch.org. diff --git a/Documentation/tutorials/ovn-rbac.rst b/Documentation/tutorials/ovn-rbac.rst new file mode 100644 index 000000000..ec163e2df --- /dev/null +++ b/Documentation/tutorials/ovn-rbac.rst @@ -0,0 +1,134 @@ +.. + Licensed under the Apache License, Version 2.0 (the "License"); you may + not use this file except in compliance with the License. You may obtain + a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + License for the specific language governing permissions and limitations + under the License. + + Convention for heading levels in Open vSwitch documentation: + + ======= Heading 0 (reserved for the title in a document) + ------- Heading 1 + ~~~~~~~ Heading 2 + +++++++ Heading 3 + ''''''' Heading 4 + + Avoid deeper levels because they do not render well. + +============================================= +OVN Role-Based Access Control (RBAC) Tutorial +============================================= + +This document provides a step-by-step guide for setting up role-based access +control (RBAC) in OVN. In OVN, hypervisors (chassis) read and write the +southbound database to do configuration. Without restricting hypervisor's +access to the southbound database, a compromised hypervisor might disrupt the +entire OVN deployment by corrupting the database. RBAC ensures that each +hypervisor can only modify its own data and thus improves the security of OVN. +More details about the RBAC design can be found in ``ovn-architecture``\(7) +manpage. + +This document assumes OVN is installed in your system and runs normally. + +.. _gen-certs-keys: + +Generating Certificates and Keys +-------------------------------- + +In the OVN RBAC deployment, ovn-controller connects to the southbound database +via SSL connection. The southbound database uses CA-signed certificate to +authenticate ovn-controller. + +Suppose there are three machines in your deployment. `machine_1` runs +`chassis_1` and has IP address `machine_1-ip`. `machine_2` runs `chassis_2` and +has IP address `machine_2-ip`. `machine_3` hosts southbound database and has IP +address `machine_3-ip`. `machine_3` also hosts public key infrastructure (PKI). + +1. Initiate PKI. + + In `machine_3`:: + + $ ovs-pki init + +2. Generate southbound database's certificate request. Sign the certificate + request with the CA key. + + In `machine_3`:: + + $ ovs-pki req -u sbdb + $ ovs-pki sign sbdb switch + +3. Generate chassis certificate requests. Copy the certificate requests to + `machine_3`. + + In `machine_1`:: + + $ ovs-pki req -u chassis_1 + $ scp chassis_1-req.pem \ + machine_3@machine_3-ip:/path/to/chassis_1-req.pem + + In `machine_2`:: + + $ ovs-pki req -u chassis_2 + $ scp chassis_2-req.pem \ + machine_3@machine_3-ip:/path/to/chassis_2-req.pem + + .. note:: + + chassis_1 must be the same string as ``external_ids:system-id`` in the + Open_vSwitch table (the chassis name) of machine_1. Same applies for + chassis_2. + +4. Sign the chassis certificate requests with the CA key. Copy `chassis_1`'s + signed certificate and the CA certificate to `machine_1`. Copy `chassis_2`'s + signed certificate and the CA certificate to `machine_2`. + + In `machine_3`:: + + $ ovs-pki sign chassis_1 switch + $ ovs-pki sign chassis_2 switch + $ scp chassis_1-cert.pem \ + machine_1@machine_1-ip:/path/to/chassis_1-cert.pem + $ scp /var/lib/openvswitch/pki/switchca/cacert.pem \ + machine_1@machine_1-ip:/path/to/cacert.pem + $ scp chassis_2-cert.pem \ + machine_2@machine_2-ip:/path/to/chassis_2-cert.pem + $ scp /var/lib/openvswitch/pki/switchca/cacert.pem \ + machine_2@machine_2-ip:/path/to/cacert.pem + +Configuring RBAC +---------------- + +1. Set certificate, private key, and CA certificate for the southbound + database. Configure the southbound database to listen on SSL connection and + enforce role-based access control. + + In `machine_3`:: + + $ ovn-sbctl set-ssl /path/to/sbdb-privkey.pem \ + /path/to/sbdb-cert.pem /path/to/cacert.pem + $ ovn-sbctl set-connection role=ovn-controller pssl:6642 + +2. Set certificate, private key, and CA certificate for `chassis_1` and + `chassis_2`. Configure `chassis_1` and `chassis_2` to connect southbound + database via SSL. + + In `machine_1`:: + + $ ovs-vsctl set-ssl /path/to/chassis_1-privkey.pem \ + /path/to/chassis_1-cert.pem /path/to/cacert.pem + $ ovs-vsctl set open_vswitch . \ + external_ids:ovn-remote=ssl:machine_3-ip:6642 + + In `machine_2`:: + + $ ovs-vsctl set-ssl /path/to/chassis_2-privkey.pem \ + /path/to/chassis_2-cert.pem /path/to/cacert.pem + $ ovs-vsctl set open_vswitch . \ + external_ids:ovn-remote=ssl:machine_3-ip:6642