From 3e64ff9251fb9c1b332f2faf2f98385cad884192 Mon Sep 17 00:00:00 2001 From: Ad Schellevis Date: Thu, 3 Feb 2022 09:35:06 +0000 Subject: [PATCH] WIP net/frr: Add BGP password support (#2800) Add BGP password support [https://github.com/opnsense/plugins/pull/2645] Try to figure out which entries belong to FRR before removing them, so neighbour changes won't be left on the machine after apply. Flush our desired configuration into /usr/local/etc/frr/sa_policies.conf for easy reading and testing. Since we don't know if passwords have changed, we will have to drop SA's first. When this is a bit bumpy, we may also try to alter the existing SA's, this shouldn't be too hard to add later on. Co-authored-by: Michael --- net/frr/pkg-descr | 1 + .../OPNsense/Quagga/Api/BgpController.php | 2 + .../Quagga/forms/dialogEditBGPNeighbor.xml | 14 ++++ .../mvc/app/models/OPNsense/Quagga/BGP.xml | 6 ++ net/frr/src/opnsense/scripts/frr/register_sas | 68 +++++++++++++++++++ net/frr/src/opnsense/scripts/quagga/setup.sh | 3 + .../templates/OPNsense/Quagga/+TARGETS | 1 + .../templates/OPNsense/Quagga/bgpd.conf | 3 + .../OPNsense/Quagga/sa_policies.conf | 24 +++++++ 9 files changed, 122 insertions(+) create mode 100755 net/frr/src/opnsense/scripts/frr/register_sas create mode 100644 net/frr/src/opnsense/service/templates/OPNsense/Quagga/sa_policies.conf diff --git a/net/frr/pkg-descr b/net/frr/pkg-descr index 77b799b58..a8ef2122d 100644 --- a/net/frr/pkg-descr +++ b/net/frr/pkg-descr @@ -14,6 +14,7 @@ Plugin Changelog 1.26 * Fix Model migration errors +* Add BGP password authentication 1.25 diff --git a/net/frr/src/opnsense/mvc/app/controllers/OPNsense/Quagga/Api/BgpController.php b/net/frr/src/opnsense/mvc/app/controllers/OPNsense/Quagga/Api/BgpController.php index 8eb6bd129..9f505632e 100644 --- a/net/frr/src/opnsense/mvc/app/controllers/OPNsense/Quagga/Api/BgpController.php +++ b/net/frr/src/opnsense/mvc/app/controllers/OPNsense/Quagga/Api/BgpController.php @@ -45,6 +45,8 @@ class BgpController extends ApiMutableModelControllerBase "description", "address", "remoteas", + "password", + "localip", "updatesource", "nexthopself", "multihop", diff --git a/net/frr/src/opnsense/mvc/app/controllers/OPNsense/Quagga/forms/dialogEditBGPNeighbor.xml b/net/frr/src/opnsense/mvc/app/controllers/OPNsense/Quagga/forms/dialogEditBGPNeighbor.xml index d9f7f012d..8665c2bce 100644 --- a/net/frr/src/opnsense/mvc/app/controllers/OPNsense/Quagga/forms/dialogEditBGPNeighbor.xml +++ b/net/frr/src/opnsense/mvc/app/controllers/OPNsense/Quagga/forms/dialogEditBGPNeighbor.xml @@ -22,6 +22,20 @@ text Neighbor AS. + + neighbor.password + + text + true + Set a password for BGP authentication. + + + neighbor.localip + + text + true + Set the local IP connecting to the neighbor. This is only required for BGP authentication. + neighbor.updatesource diff --git a/net/frr/src/opnsense/mvc/app/models/OPNsense/Quagga/BGP.xml b/net/frr/src/opnsense/mvc/app/models/OPNsense/Quagga/BGP.xml index 3a052bd4e..33dd74147 100644 --- a/net/frr/src/opnsense/mvc/app/models/OPNsense/Quagga/BGP.xml +++ b/net/frr/src/opnsense/mvc/app/models/OPNsense/Quagga/BGP.xml @@ -57,6 +57,12 @@ 1 4294967295 + + N + + + N + N diff --git a/net/frr/src/opnsense/scripts/frr/register_sas b/net/frr/src/opnsense/scripts/frr/register_sas new file mode 100755 index 000000000..a56c07ef6 --- /dev/null +++ b/net/frr/src/opnsense/scripts/frr/register_sas @@ -0,0 +1,68 @@ +#!/usr/local/bin/python3 +""" + Copyright (c) 2022 Ad Schellevis + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +""" +import os +import subprocess +import tempfile +from configparser import ConfigParser + +if __name__ == '__main__': + frr_sad = {} + frr_sa_database = "/usr/local/etc/frr/sa_policies.conf" + # stage 1, read required FRR policies + if os.path.exists(frr_sa_database): + cnf = ConfigParser() + cnf.read(frr_sa_database) + for section in cnf.sections(): + if cnf.has_option(section, 'src') and cnf.has_option(section, 'dst'): + policy_key = "%s %s" % (cnf.get(section, 'src'), cnf.get(section, 'dst')) + frr_sad[policy_key] = {} + for prop in cnf.items(section): + frr_sad[policy_key][prop[0]] = prop[1] + + # stage 2, red current installed policies which seems to originate from FRR + registered_policies = [] + current_policy = None + for line in subprocess.run(["/sbin/setkey", "-D"], capture_output=True, text=True).stdout.split('\n'): + parts = line.strip().split() + if not line.startswith('\t') and len(parts) > 1: + current_policy = {"src": parts[0], "dst": parts[1]} + elif len(parts) > 2 and parts[0] == 'A:' and parts[1] == 'tcp-md5': + # Let's assume we're the only ones registering these types of entries + registered_policies.append(current_policy) + + # flush changes to temp file and load with setkey + temp_filename = None + with tempfile.NamedTemporaryFile(mode='wt', delete=False) as fo: + temp_filename = fo.name + for policy in registered_policies: + fo.write("delete -4 %(src)s %(dst)s tcp 0x1000;\n" % policy) + for new_policy in frr_sad: + fo.write('add -4 %(src)s %(dst)s %(protocol)s %(spi)s -A %(aalgo)s "%(key)s";\n' % frr_sad[new_policy]) + + if temp_filename: + subprocess.run(["/sbin/setkey", "-f", fo.name], capture_output=True, text=True) diff --git a/net/frr/src/opnsense/scripts/quagga/setup.sh b/net/frr/src/opnsense/scripts/quagga/setup.sh index 5cb140a6b..f91e271fd 100755 --- a/net/frr/src/opnsense/scripts/quagga/setup.sh +++ b/net/frr/src/opnsense/scripts/quagga/setup.sh @@ -18,3 +18,6 @@ chown -R $user:$group /var/run/frr # logfile (if used) touch /var/log/frr.log chown $user:$group /var/log/frr.log + +# register Security Associations +/usr/local/opnsense/scripts/frr/register_sas diff --git a/net/frr/src/opnsense/service/templates/OPNsense/Quagga/+TARGETS b/net/frr/src/opnsense/service/templates/OPNsense/Quagga/+TARGETS index 30b11bedb..c2e0542d6 100644 --- a/net/frr/src/opnsense/service/templates/OPNsense/Quagga/+TARGETS +++ b/net/frr/src/opnsense/service/templates/OPNsense/Quagga/+TARGETS @@ -4,6 +4,7 @@ ospfd.conf:/usr/local/etc/frr/ospfd.conf ospfd_carp.conf:/usr/local/etc/frr/ospfd_carp.conf ospf6d.conf:/usr/local/etc/frr/ospf6d.conf ripd.conf:/usr/local/etc/frr/ripd.conf +sa_policies.conf:/usr/local/etc/frr/sa_policies.conf frr:/etc/rc.conf.d/frr zebra.conf:/usr/local/etc/frr/zebra.conf vtysh.conf:/usr/local/etc/frr/vtysh.conf diff --git a/net/frr/src/opnsense/service/templates/OPNsense/Quagga/bgpd.conf b/net/frr/src/opnsense/service/templates/OPNsense/Quagga/bgpd.conf index 3f43144fd..499ffe8e1 100644 --- a/net/frr/src/opnsense/service/templates/OPNsense/Quagga/bgpd.conf +++ b/net/frr/src/opnsense/service/templates/OPNsense/Quagga/bgpd.conf @@ -57,6 +57,9 @@ router bgp {{ OPNsense.quagga.bgp.asnumber }} {% if 'bfd' in neighbor and neighbor.bfd == '1' %} neighbor {{ neighbor.address }} bfd {% endif %} +{% if 'password' in neighbor and neighbor.password != '' %} + neighbor {{ neighbor.address }} password {{ neighbor.password }} +{% endif %} {% if ':' not in neighbor.address and 'updatesource' in neighbor and neighbor.updatesource != '' %} neighbor {{ neighbor.address }} update-source {{ physical_interface(neighbor.updatesource) }} {% endif %} diff --git a/net/frr/src/opnsense/service/templates/OPNsense/Quagga/sa_policies.conf b/net/frr/src/opnsense/service/templates/OPNsense/Quagga/sa_policies.conf new file mode 100644 index 000000000..bb1587cf3 --- /dev/null +++ b/net/frr/src/opnsense/service/templates/OPNsense/Quagga/sa_policies.conf @@ -0,0 +1,24 @@ +{% if helpers.exists('OPNsense.quagga.bgp.enabled') and OPNsense.quagga.bgp.enabled == '1' %} +{% if helpers.exists('OPNsense.quagga.bgp.neighbors.neighbor') %} +{% for neighbor in helpers.toList('OPNsense.quagga.bgp.neighbors.neighbor') %} +{% if neighbor.enabled == '1' and neighbor.password|default('') != '' %} +[policy_{{neighbor['@uuid']}}_in] +src={{ neighbor.address }} +dst={{ neighbor.localip }} +protocol=tcp +spi=0x1000 +aalgo=tcp-md5 +key={{ neighbor.password }} + +[policy_{{neighbor['@uuid']}}_out] +src={{ neighbor.localip }} +dst={{ neighbor.address }} +protocol=tcp +spi=0x1000 +aalgo=tcp-md5 +key={{ neighbor.password }} + +{% endif %} +{% endfor %} +{% endif %} +{% endif %}