diff --git a/data/templates/conntrack/nftables-helpers.j2 b/data/templates/conntrack/nftables-helpers.j2 index 433931162..63a0cc855 100644 --- a/data/templates/conntrack/nftables-helpers.j2 +++ b/data/templates/conntrack/nftables-helpers.j2 @@ -1,70 +1,76 @@ {% macro conntrack_helpers(module_map, modules, ipv4=True) %} {% if modules.ftp is vyos_defined %} ct helper ftp_tcp { type "ftp" protocol tcp; } {% endif %} {% if modules.h323 is vyos_defined %} ct helper ras_udp { type "RAS" protocol udp; } ct helper q931_tcp { type "Q.931" protocol tcp; } {% endif %} {% if modules.pptp is vyos_defined and ipv4 %} ct helper pptp_tcp { type "pptp" protocol tcp; } {% endif %} {% if modules.nfs is vyos_defined %} ct helper rpc_tcp { type "rpc" protocol tcp; } ct helper rpc_udp { type "rpc" protocol udp; } {% endif %} +{% if modules.rtsp is vyos_defined and ipv4 %} + ct helper rtsp_tcp { + type "rtsp" protocol tcp; + } +{% endif %} + {% if modules.sip is vyos_defined %} ct helper sip_tcp { type "sip" protocol tcp; } ct helper sip_udp { type "sip" protocol udp; } {% endif %} {% if modules.tftp is vyos_defined %} ct helper tftp_udp { type "tftp" protocol udp; } {% endif %} {% if modules.sqlnet is vyos_defined %} ct helper tns_tcp { type "tns" protocol tcp; } {% endif %} chain VYOS_CT_HELPER { {% for module, module_conf in module_map.items() %} {% if modules[module] is vyos_defined %} {% if 'nftables' in module_conf %} {% if module_conf.ipv4 is not vyos_defined or module_conf.ipv4 == ipv4 %} {% for rule in module_conf.nftables %} {{ rule }} {% endfor %} {% endif %} {% endif %} {% endif %} {% endfor %} return } {% endmacro %} diff --git a/debian/control b/debian/control index dddc4e14c..c5a60f660 100644 --- a/debian/control +++ b/debian/control @@ -1,336 +1,339 @@ Source: vyos-1x Section: contrib/net Priority: extra Maintainer: VyOS Package Maintainers <maintainers@vyos.net> Build-Depends: debhelper (>= 9), dh-python, fakeroot, gcc, iproute2, libvyosconfig0 (>= 0.0.7), libzmq3-dev, python3 (>= 3.10), # For generating command definitions python3-lxml, python3-xmltodict, # For running tests python3-coverage, python3-netifaces, python3-nose, python3-jinja2, python3-psutil, python3-setuptools, python3-sphinx, quilt, whois Standards-Version: 3.9.6 Package: vyos-1x Architecture: amd64 arm64 Pre-Depends: libnss-tacplus [amd64], libpam-tacplus [amd64], libpam-radius-auth [amd64] Depends: ## Fundamentals ${python3:Depends} (>= 3.10), libvyosconfig0, vyatta-bash, vyatta-cfg, vyos-http-api-tools, vyos-utils, ## End of Fundamentals ## Python libraries used in multiple modules and scripts python3, python3-cryptography, python3-hurry.filesize, python3-inotify, python3-jinja2, python3-jmespath, python3-netaddr, python3-netifaces, python3-paramiko, python3-passlib, python3-psutil, python3-pyhumps, python3-pystache, python3-pyudev, python3-six, python3-tabulate, python3-voluptuous, python3-xmltodict, python3-zmq, ## End of Python libraries ## Basic System services and utilities sudo, systemd, bsdmainutils, openssl, curl, dbus, file, iproute2 (>= 6.0.0), linux-cpupower, # ipaddrcheck is widely used in IP value validators ipaddrcheck, ethtool, fdisk, lm-sensors, procps, netplug, sed, ssl-cert, tuned, beep, wide-dhcpv6-client, # Generic colorizer grc, ## End of System services and utilities ## For the installer # Image signature verification tool minisign, # Live filesystem tools squashfs-tools, fuse-overlayfs, ## End installer auditd, iputils-arping, isc-dhcp-client, # For "vpn pptp", "vpn l2tp", "vpn sstp", "service ipoe-server" accel-ppp, # End "vpn pptp", "vpn l2tp", "vpn sstp", "service ipoe-server" avahi-daemon, conntrack, conntrackd, ## Conf mode features # For "interfaces wireless" hostapd, hsflowd, iw, wireless-regdb, wpasupplicant (>= 0.6.7), # End "interfaces wireless" # For "interfaces wwan" modemmanager, usb-modeswitch, libqmi-utils, # End "interfaces wwan" # For "interfaces openvpn" openvpn, openvpn-auth-ldap, openvpn-auth-radius, openvpn-otp, libpam-google-authenticator, # End "interfaces openvpn" # For "interfaces wireguard" wireguard-tools, qrencode, # End "interfaces wireguard" # For "interfaces pppoe" pppoe, # End "interfaces pppoe" # For "interfaces sstpc" sstp-client, # End "interfaces sstpc" # For "protocols *" frr (>= 7.5), frr-pythontools, frr-rpki-rtrlib, frr-snmp, # End "protocols *" # For "protocols nhrp" (part of DMVPN) opennhrp, # End "protocols nhrp" # For "protocols igmp-proxy" igmpproxy, # End "protocols igmp-proxy" # For "pki" certbot, # End "pki" # For "service console-server" conserver-client, conserver-server, console-data, dropbear, # End "service console-server" # For "service aws glb" aws-gwlbtun, # For "service dns dynamic" ddclient (>= 3.11.1), # End "service dns dynamic" # # For "service ids" fastnetmon [amd64], # End "service ids" # # For "service ndp-proxy" ndppd, # End "service ndp-proxy" # For "service router-advert" radvd, # End "service route-advert" # For "high-availability reverse-proxy" haproxy, # End "high-availability reverse-proxy" # For "service dhcp-relay" isc-dhcp-relay, # For "service dhcp-server" kea, # End "service dhcp-server" # For "service lldp" lldpd, # End "service lldp" # For "service https" nginx-light, # End "service https" # For "service ssh" openssh-server, sshguard, # End "service ssh" # For "service salt-minion" salt-minion, # End "service salt-minion" # For "service snmp" snmp, snmpd, # End "service snmp" # For "service upnp" miniupnpd-nftables, # End "service upnp" # For "service webproxy" squid, squidclient, squidguard, # End "service webproxy" # For "service monitoring telegraf" telegraf (>= 1.20), # End "service monitoring telegraf" # For "service monitoring zabbix-agent" zabbix-agent2, # End "service monitoring zabbix-agent" # For "service tftp-server" tftpd-hpa, # End "service tftp-server" # For "service dns forwarding" pdns-recursor, # End "service dns forwarding" # For "service sla owamp" owamp-client, owamp-server, # End "service sla owamp" # For "service sla twamp" twamp-client, twamp-server, # End "service sla twamp" # For "service broadcast-relay" udp-broadcast-relay, # End "service broadcast-relay" # For "high-availability vrrp" keepalived (>=2.0.5), # End "high-availability-vrrp" # For "system task-scheduler" cron, # End "system task-scheduler" # For "system lcd" lcdproc, lcdproc-extra-drivers, # End "system lcd" # For "system config-management commit-archive" git, # End "system config-management commit-archive" # For firewall libndp-tools, libnetfilter-conntrack3, libnfnetlink0, nfct, nftables (>= 0.9.3), # For "vpn ipsec" strongswan (>= 5.9), strongswan-swanctl (>= 5.9), charon-systemd, libcharon-extra-plugins (>=5.9), libcharon-extauth-plugins (>=5.9), libstrongswan-extra-plugins (>=5.9), libstrongswan-standard-plugins (>=5.9), python3-vici (>= 5.7.2), # End "vpn ipsec" # For "nat64" jool, # End "nat64" +# For "system conntrack modules rtsp" + nat-rtsp, +# End "system conntrack modules rtsp" # For "system ntp" chrony, # End "system ntp" # For "vpn openconnect" ocserv, # End "vpn openconnect" # For "system flow-accounting" pmacct (>= 1.6.0), # End "system flow-accounting" # For container podman, netavark, aardvark-dns, # iptables is only used for containers now, not the the firewall CLI iptables, # End container ## End Configuration mode ## Operational mode # Used for hypervisor model in "run show version" hvinfo, # For "run traceroute" traceroute, # For "run monitor traffic" tcpdump, # End "run monitor traffic" # For "show hardware dmi" dmidecode, # For "run show hardware storage smart" smartmontools, # For "run show hardware scsi" lsscsi, # For "run show hardware pci" pciutils, # For "show hardware usb" usbutils, # For "run show hardware storage nvme" nvme-cli, # For "run monitor bandwidth-test" iperf, iperf3, # End "run monitor bandwidth-test" # For "run wake-on-lan" etherwake, # For "run force ipv6-nd" ndisc6, # For "run monitor bandwidth" bmon, # End Operational mode ## TPM tools cryptsetup, tpm2-tools, ## End TPM tools ## Optional utilities easy-rsa, tcptraceroute, mtr-tiny, telnet, stunnel4, uidmap ## End optional utilities Description: VyOS configuration scripts and data VyOS configuration scripts, interface definitions, and everything Package: vyos-1x-vmware Architecture: amd64 Depends: vyos-1x, open-vm-tools Description: VyOS configuration scripts and data for VMware Adds configuration files required for VyOS running on VMware hosts. Package: vyos-1x-smoketest Architecture: all Depends: skopeo, snmp, vyos-1x Description: VyOS build sanity checking toolkit diff --git a/interface-definitions/include/firewall/conntrack-helper.xml.i b/interface-definitions/include/firewall/conntrack-helper.xml.i index ee17f2c61..3ca1a0353 100644 --- a/interface-definitions/include/firewall/conntrack-helper.xml.i +++ b/interface-definitions/include/firewall/conntrack-helper.xml.i @@ -1,42 +1,46 @@ <!-- include start from firewall/conntrack-helper.xml.i --> <leafNode name="conntrack-helper"> <properties> <help>Match related traffic from conntrack helpers</help> <completionHelp> <list>ftp h323 pptp nfs sip tftp sqlnet</list> </completionHelp> <valueHelp> <format>ftp</format> <description>Related traffic from FTP helper</description> </valueHelp> <valueHelp> <format>h323</format> <description>Related traffic from H.323 helper</description> </valueHelp> <valueHelp> <format>pptp</format> <description>Related traffic from PPTP helper</description> </valueHelp> <valueHelp> <format>nfs</format> <description>Related traffic from NFS helper</description> </valueHelp> + <valueHelp> + <format>rtsp</format> + <description>Related traffic from RTSP helper</description> + </valueHelp> <valueHelp> <format>sip</format> <description>Related traffic from SIP helper</description> </valueHelp> <valueHelp> <format>tftp</format> <description>Related traffic from TFTP helper</description> </valueHelp> <valueHelp> <format>sqlnet</format> <description>Related traffic from SQLNet helper</description> </valueHelp> <constraint> - <regex>(ftp|h323|pptp|nfs|sip|tftp|sqlnet)</regex> + <regex>(ftp|h323|pptp|nfs|rtsp|sip|tftp|sqlnet)</regex> </constraint> <multi/> </properties> </leafNode> <!-- include end --> diff --git a/interface-definitions/system_conntrack.xml.in b/interface-definitions/system_conntrack.xml.in index a348097cc..219c6e28e 100644 --- a/interface-definitions/system_conntrack.xml.in +++ b/interface-definitions/system_conntrack.xml.in @@ -1,513 +1,519 @@ <?xml version="1.0"?> <interfaceDefinition> <node name="system"> <children> <node name="conntrack" owner="${vyos_conf_scripts_dir}/system_conntrack.py"> <properties> <help>Connection Tracking Engine Options</help> <!-- Before NAT and conntrack-sync are configured --> <priority>218</priority> </properties> <children> <leafNode name="flow-accounting"> <properties> <help>Enable connection tracking flow accounting</help> <valueless/> </properties> </leafNode> <leafNode name="expect-table-size"> <properties> <help>Size of connection tracking expect table</help> <valueHelp> <format>u32:1-50000000</format> <description>Number of entries allowed in connection tracking expect table</description> </valueHelp> <constraint> <validator name="numeric" argument="--range 1-50000000"/> </constraint> </properties> <defaultValue>2048</defaultValue> </leafNode> <leafNode name="hash-size"> <properties> <help>Hash size for connection tracking table</help> <valueHelp> <format>u32:1-50000000</format> <description>Size of hash to use for connection tracking table</description> </valueHelp> <constraint> <validator name="numeric" argument="--range 1-50000000"/> </constraint> </properties> <defaultValue>32768</defaultValue> </leafNode> <node name="ignore"> <properties> <help>Customized rules to ignore selective connection tracking</help> </properties> <children> <node name="ipv4"> <properties> <help>IPv4 rules</help> </properties> <children> <tagNode name="rule"> <properties> <help>Rule number</help> <valueHelp> <format>u32:1-999999</format> <description>Number of conntrack ignore rule</description> </valueHelp> <constraint> <validator name="numeric" argument="--range 1-999999"/> </constraint> <constraintErrorMessage>Ignore rule number must be between 1 and 999999</constraintErrorMessage> </properties> <children> #include <include/generic-description.xml.i> <node name="destination"> <properties> <help>Destination parameters</help> </properties> <children> #include <include/firewall/source-destination-group-ipv4.xml.i> #include <include/nat-address.xml.i> #include <include/nat-port.xml.i> </children> </node> <leafNode name="inbound-interface"> <properties> <help>Interface to ignore connections tracking on</help> <completionHelp> <list>any</list> <script>${vyos_completion_dir}/list_interfaces</script> </completionHelp> </properties> </leafNode> #include <include/ip-protocol.xml.i> <leafNode name="protocol"> <properties> <help>Protocol to match (protocol name, number, or "all")</help> <completionHelp> <script>${vyos_completion_dir}/list_protocols.sh</script> <list>all tcp_udp</list> </completionHelp> <valueHelp> <format>all</format> <description>All IP protocols</description> </valueHelp> <valueHelp> <format>tcp_udp</format> <description>Both TCP and UDP</description> </valueHelp> <valueHelp> <format>u32:0-255</format> <description>IP protocol number</description> </valueHelp> <valueHelp> <format><protocol></format> <description>IP protocol name</description> </valueHelp> <valueHelp> <format>!<protocol></format> <description>IP protocol name</description> </valueHelp> <constraint> <validator name="ip-protocol"/> </constraint> </properties> </leafNode> <node name="source"> <properties> <help>Source parameters</help> </properties> <children> #include <include/firewall/source-destination-group-ipv4.xml.i> #include <include/nat-address.xml.i> #include <include/nat-port.xml.i> </children> </node> #include <include/firewall/tcp-flags.xml.i> </children> </tagNode> </children> </node> <node name="ipv6"> <properties> <help>IPv6 rules</help> </properties> <children> <tagNode name="rule"> <properties> <help>Rule number</help> <valueHelp> <format>u32:1-999999</format> <description>Number of conntrack ignore rule</description> </valueHelp> <constraint> <validator name="numeric" argument="--range 1-999999"/> </constraint> <constraintErrorMessage>Ignore rule number must be between 1 and 999999</constraintErrorMessage> </properties> <children> #include <include/generic-description.xml.i> <node name="destination"> <properties> <help>Destination parameters</help> </properties> <children> #include <include/firewall/address-ipv6.xml.i> #include <include/firewall/source-destination-group-ipv6.xml.i> #include <include/nat-port.xml.i> </children> </node> <leafNode name="inbound-interface"> <properties> <help>Interface to ignore connections tracking on</help> <completionHelp> <list>any</list> <script>${vyos_completion_dir}/list_interfaces</script> </completionHelp> </properties> </leafNode> #include <include/ip-protocol.xml.i> <leafNode name="protocol"> <properties> <help>Protocol to match (protocol name, number, or "all")</help> <completionHelp> <script>${vyos_completion_dir}/list_protocols.sh</script> <list>all tcp_udp</list> </completionHelp> <valueHelp> <format>all</format> <description>All IP protocols</description> </valueHelp> <valueHelp> <format>tcp_udp</format> <description>Both TCP and UDP</description> </valueHelp> <valueHelp> <format>u32:0-255</format> <description>IP protocol number</description> </valueHelp> <valueHelp> <format><protocol></format> <description>IP protocol name</description> </valueHelp> <valueHelp> <format>!<protocol></format> <description>IP protocol name</description> </valueHelp> <constraint> <validator name="ip-protocol"/> </constraint> </properties> </leafNode> <node name="source"> <properties> <help>Source parameters</help> </properties> <children> #include <include/firewall/address-ipv6.xml.i> #include <include/firewall/source-destination-group-ipv6.xml.i> #include <include/nat-port.xml.i> </children> </node> #include <include/firewall/tcp-flags.xml.i> </children> </tagNode> </children> </node> </children> </node> <node name="log"> <properties> <help>Log connection tracking events per protocol</help> </properties> <children> <node name="icmp"> <properties> <help>Log connection tracking events for ICMP</help> </properties> <children> #include <include/conntrack/log-common.xml.i> </children> </node> <node name="other"> <properties> <help>Log connection tracking events for all protocols other than TCP, UDP and ICMP</help> </properties> <children> #include <include/conntrack/log-common.xml.i> </children> </node> <node name="tcp"> <properties> <help>Log connection tracking events for TCP</help> </properties> <children> #include <include/conntrack/log-common.xml.i> </children> </node> <node name="udp"> <properties> <help>Log connection tracking events for UDP</help> </properties> <children> #include <include/conntrack/log-common.xml.i> </children> </node> </children> </node> <node name="modules"> <properties> <help>Connection tracking modules</help> </properties> <children> <leafNode name="ftp"> <properties> <help>FTP connection tracking</help> <valueless/> </properties> </leafNode> <leafNode name="h323"> <properties> <help>H.323 connection tracking</help> <valueless/> </properties> </leafNode> <leafNode name="nfs"> <properties> <help>NFS connection tracking</help> <valueless/> </properties> </leafNode> <leafNode name="pptp"> <properties> <help>PPTP connection tracking</help> <valueless/> </properties> </leafNode> + <leafNode name="rtsp"> + <properties> + <help>RTSP connection tracking</help> + <valueless/> + </properties> + </leafNode> <leafNode name="sip"> <properties> <help>SIP connection tracking</help> <valueless/> </properties> </leafNode> <leafNode name="sqlnet"> <properties> <help>SQLnet connection tracking</help> <valueless/> </properties> </leafNode> <leafNode name="tftp"> <properties> <help>TFTP connection tracking</help> <valueless/> </properties> </leafNode> </children> </node> <leafNode name="table-size"> <properties> <help>Size of connection tracking table</help> <valueHelp> <format>u32:1-50000000</format> <description>Number of entries allowed in connection tracking table</description> </valueHelp> <constraint> <validator name="numeric" argument="--range 1-50000000"/> </constraint> </properties> <defaultValue>262144</defaultValue> </leafNode> <node name="tcp"> <properties> <help>TCP options</help> </properties> <children> <leafNode name="half-open-connections"> <properties> <help>Maximum number of TCP half-open connections</help> <valueHelp> <format>u32:1-2147483647</format> <description>Generic connection timeout in seconds</description> </valueHelp> <constraint> <validator name="numeric" argument="--range 1-2147483647"/> </constraint> </properties> <defaultValue>512</defaultValue> </leafNode> <leafNode name="loose"> <properties> <help>Policy to track previously established connections</help> <completionHelp> <list>enable disable</list> </completionHelp> <valueHelp> <format>enable</format> <description>Allow tracking of previously established connections</description> </valueHelp> <valueHelp> <format>disable</format> <description>Do not allow tracking of previously established connections</description> </valueHelp> <constraint> <regex>(enable|disable)</regex> </constraint> </properties> <defaultValue>enable</defaultValue> </leafNode> <leafNode name="max-retrans"> <properties> <help>Maximum number of packets that can be retransmitted without received an ACK</help> <valueHelp> <format>u32:1-255</format> <description>Number of packets to be retransmitted</description> </valueHelp> <constraint> <validator name="numeric" argument="--range 1-255"/> </constraint> </properties> <defaultValue>3</defaultValue> </leafNode> </children> </node> <node name="timeout"> <properties> <help>Connection timeout options</help> </properties> <children> <node name="custom"> <properties> <help>Define custom timeouts per connection</help> </properties> <children> <node name="ipv4"> <properties> <help>IPv4 rules</help> </properties> <children> <tagNode name="rule"> <properties> <help>Rule number</help> <valueHelp> <format>u32:1-999999</format> <description>Number of conntrack rule</description> </valueHelp> <constraint> <validator name="numeric" argument="--range 1-999999"/> </constraint> <constraintErrorMessage>Ignore rule number must be between 1 and 999999</constraintErrorMessage> </properties> <children> #include <include/generic-description.xml.i> <node name="destination"> <properties> <help>Destination parameters</help> </properties> <children> #include <include/nat-address.xml.i> #include <include/nat-port.xml.i> </children> </node> <leafNode name="inbound-interface"> <properties> <help>Interface to ignore connections tracking on</help> <completionHelp> <list>any</list> <script>${vyos_completion_dir}/list_interfaces</script> </completionHelp> </properties> </leafNode> <node name="protocol"> <properties> <help>Customize protocol specific timers, one protocol configuration per rule</help> </properties> <children> #include <include/conntrack/timeout-custom-protocols.xml.i> </children> </node> <node name="source"> <properties> <help>Source parameters</help> </properties> <children> #include <include/nat-address.xml.i> #include <include/nat-port.xml.i> </children> </node> </children> </tagNode> </children> </node> <node name="ipv6"> <properties> <help>IPv6 rules</help> </properties> <children> <tagNode name="rule"> <properties> <help>Rule number</help> <valueHelp> <format>u32:1-999999</format> <description>Number of conntrack rule</description> </valueHelp> <constraint> <validator name="numeric" argument="--range 1-999999"/> </constraint> <constraintErrorMessage>Ignore rule number must be between 1 and 999999</constraintErrorMessage> </properties> <children> #include <include/generic-description.xml.i> <node name="destination"> <properties> <help>Destination parameters</help> </properties> <children> #include <include/firewall/address-ipv6.xml.i> #include <include/nat-port.xml.i> </children> </node> <leafNode name="inbound-interface"> <properties> <help>Interface to ignore connections tracking on</help> <completionHelp> <list>any</list> <script>${vyos_completion_dir}/list_interfaces</script> </completionHelp> </properties> </leafNode> <node name="protocol"> <properties> <help>Customize protocol specific timers, one protocol configuration per rule</help> </properties> <children> #include <include/conntrack/timeout-custom-protocols.xml.i> </children> </node> <node name="source"> <properties> <help>Source parameters</help> </properties> <children> #include <include/firewall/address-ipv6.xml.i> #include <include/nat-port.xml.i> </children> </node> </children> </tagNode> </children> </node> </children> </node> #include <include/conntrack/timeout-common-protocols.xml.i> </children> </node> </children> </node> </children> </node> </interfaceDefinition> diff --git a/smoketest/scripts/cli/test_system_conntrack.py b/smoketest/scripts/cli/test_system_conntrack.py index f00626b3d..2d76da145 100755 --- a/smoketest/scripts/cli/test_system_conntrack.py +++ b/smoketest/scripts/cli/test_system_conntrack.py @@ -1,341 +1,345 @@ #!/usr/bin/env python3 # # Copyright (C) 2021-2023 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as # published by the Free Software Foundation. # # This program 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 # along with this program. If not, see <http://www.gnu.org/licenses/>. import os import re import unittest from base_vyostest_shim import VyOSUnitTestSHIM from vyos.firewall import find_nftables_rule from vyos.utils.file import read_file base_path = ['system', 'conntrack'] def get_sysctl(parameter): tmp = parameter.replace(r'.', r'/') return read_file(f'/proc/sys/{tmp}') class TestSystemConntrack(VyOSUnitTestSHIM.TestCase): @classmethod def setUpClass(cls): super(TestSystemConntrack, cls).setUpClass() # ensure we can also run this test on a live system - so lets clean # out the current configuration :) cls.cli_delete(cls, base_path) def tearDown(self): self.cli_delete(base_path) self.cli_commit() def test_conntrack_options(self): conntrack_config = { 'net.netfilter.nf_conntrack_expect_max' : { 'cli' : ['expect-table-size'], 'test_value' : '8192', 'default_value' : '2048', }, 'net.nf_conntrack_max' :{ 'cli' : ['table-size'], 'test_value' : '500000', 'default_value' : '262144', }, 'net.ipv4.tcp_max_syn_backlog' :{ 'cli' : ['tcp', 'half-open-connections'], 'test_value' : '2048', 'default_value' : '512', }, 'net.netfilter.nf_conntrack_tcp_loose' :{ 'cli' : ['tcp', 'loose'], 'test_value' : 'disable', 'default_value' : '1', }, 'net.netfilter.nf_conntrack_tcp_max_retrans' :{ 'cli' : ['tcp', 'max-retrans'], 'test_value' : '128', 'default_value' : '3', }, 'net.netfilter.nf_conntrack_icmp_timeout' :{ 'cli' : ['timeout', 'icmp'], 'test_value' : '180', 'default_value' : '30', }, 'net.netfilter.nf_conntrack_generic_timeout' :{ 'cli' : ['timeout', 'other'], 'test_value' : '1200', 'default_value' : '600', }, 'net.netfilter.nf_conntrack_tcp_timeout_close_wait' :{ 'cli' : ['timeout', 'tcp', 'close-wait'], 'test_value' : '30', 'default_value' : '60', }, 'net.netfilter.nf_conntrack_tcp_timeout_close' :{ 'cli' : ['timeout', 'tcp', 'close'], 'test_value' : '20', 'default_value' : '10', }, 'net.netfilter.nf_conntrack_tcp_timeout_established' :{ 'cli' : ['timeout', 'tcp', 'established'], 'test_value' : '1000', 'default_value' : '432000', }, 'net.netfilter.nf_conntrack_tcp_timeout_fin_wait' :{ 'cli' : ['timeout', 'tcp', 'fin-wait'], 'test_value' : '240', 'default_value' : '120', }, 'net.netfilter.nf_conntrack_tcp_timeout_last_ack' :{ 'cli' : ['timeout', 'tcp', 'last-ack'], 'test_value' : '300', 'default_value' : '30', }, 'net.netfilter.nf_conntrack_tcp_timeout_syn_recv' :{ 'cli' : ['timeout', 'tcp', 'syn-recv'], 'test_value' : '100', 'default_value' : '60', }, 'net.netfilter.nf_conntrack_tcp_timeout_syn_sent' :{ 'cli' : ['timeout', 'tcp', 'syn-sent'], 'test_value' : '300', 'default_value' : '120', }, 'net.netfilter.nf_conntrack_tcp_timeout_time_wait' :{ 'cli' : ['timeout', 'tcp', 'time-wait'], 'test_value' : '303', 'default_value' : '120', }, 'net.netfilter.nf_conntrack_udp_timeout' :{ 'cli' : ['timeout', 'udp', 'other'], 'test_value' : '90', 'default_value' : '30', }, 'net.netfilter.nf_conntrack_udp_timeout_stream' :{ 'cli' : ['timeout', 'udp', 'stream'], 'test_value' : '200', 'default_value' : '180', }, } for parameter, parameter_config in conntrack_config.items(): self.cli_set(base_path + parameter_config['cli'] + [parameter_config['test_value']]) # commit changes self.cli_commit() # validate configuration for parameter, parameter_config in conntrack_config.items(): tmp = parameter_config['test_value'] # net.netfilter.nf_conntrack_tcp_loose has a fancy "disable" value, # make this work if tmp == 'disable': tmp = '0' self.assertEqual(get_sysctl(f'{parameter}'), tmp) # delete all configuration options and revert back to defaults self.cli_delete(base_path) self.cli_commit() # validate configuration for parameter, parameter_config in conntrack_config.items(): self.assertEqual(get_sysctl(f'{parameter}'), parameter_config['default_value']) def test_conntrack_module_enable(self): # conntrack helper modules are disabled by default modules = { 'ftp': { 'driver': ['nf_nat_ftp', 'nf_conntrack_ftp'], 'nftables': ['ct helper set "ftp_tcp"'] }, 'h323': { 'driver': ['nf_nat_h323', 'nf_conntrack_h323'], 'nftables': ['ct helper set "ras_udp"', 'ct helper set "q931_tcp"'] }, 'nfs': { 'nftables': ['ct helper set "rpc_tcp"', 'ct helper set "rpc_udp"'] }, 'pptp': { 'driver': ['nf_nat_pptp', 'nf_conntrack_pptp'], 'nftables': ['ct helper set "pptp_tcp"'] - }, + }, + 'rtsp': { + 'driver': ['nf_nat_rtsp', 'nf_conntrack_rtsp'], + 'nftables': ['ct helper set "rtsp_tcp"'] + }, 'sip': { 'driver': ['nf_nat_sip', 'nf_conntrack_sip'], 'nftables': ['ct helper set "sip_tcp"', 'ct helper set "sip_udp"'] - }, + }, 'sqlnet': { 'nftables': ['ct helper set "tns_tcp"'] }, 'tftp': { 'driver': ['nf_nat_tftp', 'nf_conntrack_tftp'], 'nftables': ['ct helper set "tftp_udp"'] }, } # load modules for module in modules: self.cli_set(base_path + ['modules', module]) # commit changes self.cli_commit() # verify modules are loaded on the system for module, module_options in modules.items(): if 'driver' in module_options: for driver in module_options['driver']: self.assertTrue(os.path.isdir(f'/sys/module/{driver}')) if 'nftables' in module_options: for rule in module_options['nftables']: self.assertTrue(find_nftables_rule('ip vyos_conntrack', 'VYOS_CT_HELPER', [rule]) != None) # unload modules for module in modules: self.cli_delete(base_path + ['modules', module]) # commit changes self.cli_commit() # verify modules are not loaded on the system for module, module_options in modules.items(): if 'driver' in module_options: for driver in module_options['driver']: self.assertFalse(os.path.isdir(f'/sys/module/{driver}')) if 'nftables' in module_options: for rule in module_options['nftables']: self.assertTrue(find_nftables_rule('ip vyos_conntrack', 'VYOS_CT_HELPER', [rule]) == None) def test_conntrack_hash_size(self): hash_size = '65536' hash_size_default = '32768' self.cli_set(base_path + ['hash-size', hash_size]) # commit changes self.cli_commit() # verify new configuration - only effective after reboot, but # a valid config file is sufficient tmp = read_file('/etc/modprobe.d/vyatta_nf_conntrack.conf') self.assertIn(hash_size, tmp) # Test default value by deleting the configuration self.cli_delete(base_path + ['hash-size']) # commit changes self.cli_commit() # verify new configuration - only effective after reboot, but # a valid config file is sufficient tmp = read_file('/etc/modprobe.d/vyatta_nf_conntrack.conf') self.assertIn(hash_size_default, tmp) def test_conntrack_ignore(self): address_group = 'conntracktest' address_group_member = '192.168.0.1' ipv6_address_group = 'conntracktest6' ipv6_address_group_member = 'dead:beef::1' self.cli_set(['firewall', 'group', 'address-group', address_group, 'address', address_group_member]) self.cli_set(['firewall', 'group', 'ipv6-address-group', ipv6_address_group, 'address', ipv6_address_group_member]) self.cli_set(base_path + ['ignore', 'ipv4', 'rule', '1', 'source', 'address', '192.0.2.1']) self.cli_set(base_path + ['ignore', 'ipv4', 'rule', '1', 'destination', 'address', '192.0.2.2']) self.cli_set(base_path + ['ignore', 'ipv4', 'rule', '1', 'destination', 'port', '22']) self.cli_set(base_path + ['ignore', 'ipv4', 'rule', '1', 'protocol', 'tcp']) self.cli_set(base_path + ['ignore', 'ipv4', 'rule', '1', 'tcp', 'flags', 'syn']) self.cli_set(base_path + ['ignore', 'ipv4', 'rule', '2', 'source', 'address', '192.0.2.1']) self.cli_set(base_path + ['ignore', 'ipv4', 'rule', '2', 'destination', 'group', 'address-group', address_group]) self.cli_set(base_path + ['ignore', 'ipv6', 'rule', '11', 'source', 'address', 'fe80::1']) self.cli_set(base_path + ['ignore', 'ipv6', 'rule', '11', 'destination', 'address', 'fe80::2']) self.cli_set(base_path + ['ignore', 'ipv6', 'rule', '11', 'destination', 'port', '22']) self.cli_set(base_path + ['ignore', 'ipv6', 'rule', '11', 'protocol', 'tcp']) self.cli_set(base_path + ['ignore', 'ipv6', 'rule', '12', 'source', 'address', 'fe80::1']) self.cli_set(base_path + ['ignore', 'ipv6', 'rule', '12', 'destination', 'group', 'address-group', ipv6_address_group]) self.cli_set(base_path + ['ignore', 'ipv6', 'rule', '13', 'source', 'address', 'fe80::1']) self.cli_set(base_path + ['ignore', 'ipv6', 'rule', '13', 'destination', 'address', '!fe80::3']) self.cli_commit() nftables_search = [ ['ip saddr 192.0.2.1', 'ip daddr 192.0.2.2', 'tcp dport 22', 'tcp flags & syn == syn', 'notrack'], ['ip saddr 192.0.2.1', 'ip daddr @A_conntracktest', 'notrack'] ] nftables6_search = [ ['ip6 saddr fe80::1', 'ip6 daddr fe80::2', 'tcp dport 22', 'notrack'], ['ip6 saddr fe80::1', 'ip6 daddr @A6_conntracktest6', 'notrack'], ['ip6 saddr fe80::1', 'ip6 daddr != fe80::3', 'notrack'] ] self.verify_nftables(nftables_search, 'ip vyos_conntrack') self.verify_nftables(nftables6_search, 'ip6 vyos_conntrack') self.cli_delete(['firewall']) def test_conntrack_timeout_custom(self): self.cli_set(base_path + ['timeout', 'custom', 'ipv4', 'rule', '1', 'source', 'address', '192.0.2.1']) self.cli_set(base_path + ['timeout', 'custom', 'ipv4', 'rule', '1', 'destination', 'address', '192.0.2.2']) self.cli_set(base_path + ['timeout', 'custom', 'ipv4', 'rule', '1', 'destination', 'port', '22']) self.cli_set(base_path + ['timeout', 'custom', 'ipv4', 'rule', '1', 'protocol', 'tcp', 'syn-sent', '77']) self.cli_set(base_path + ['timeout', 'custom', 'ipv4', 'rule', '1', 'protocol', 'tcp', 'close', '88']) self.cli_set(base_path + ['timeout', 'custom', 'ipv4', 'rule', '1', 'protocol', 'tcp', 'established', '99']) self.cli_set(base_path + ['timeout', 'custom', 'ipv4', 'rule', '2', 'inbound-interface', 'eth1']) self.cli_set(base_path + ['timeout', 'custom', 'ipv4', 'rule', '2', 'source', 'address', '198.51.100.1']) self.cli_set(base_path + ['timeout', 'custom', 'ipv4', 'rule', '2', 'protocol', 'udp', 'unreplied', '55']) self.cli_set(base_path + ['timeout', 'custom', 'ipv6', 'rule', '1', 'source', 'address', '2001:db8::1']) self.cli_set(base_path + ['timeout', 'custom', 'ipv6', 'rule', '1', 'inbound-interface', 'eth2']) self.cli_set(base_path + ['timeout', 'custom', 'ipv6', 'rule', '1', 'protocol', 'tcp', 'time-wait', '22']) self.cli_set(base_path + ['timeout', 'custom', 'ipv6', 'rule', '1', 'protocol', 'tcp', 'last-ack', '33']) self.cli_commit() nftables_search = [ ['ct timeout ct-timeout-1 {'], ['protocol tcp'], ['policy = { syn_sent : 1m17s, established : 1m39s, close : 1m28s }'], ['ct timeout ct-timeout-2 {'], ['protocol udp'], ['policy = { unreplied : 55s }'], ['chain VYOS_CT_TIMEOUT {'], ['ip saddr 192.0.2.1', 'ip daddr 192.0.2.2', 'tcp dport 22', 'ct timeout set "ct-timeout-1"'], ['iifname "eth1"', 'meta l4proto udp', 'ip saddr 198.51.100.1', 'ct timeout set "ct-timeout-2"'] ] nftables6_search = [ ['ct timeout ct-timeout-1 {'], ['protocol tcp'], ['policy = { last_ack : 33s, time_wait : 22s }'], ['chain VYOS_CT_TIMEOUT {'], ['iifname "eth2"', 'meta l4proto tcp', 'ip6 saddr 2001:db8::1', 'ct timeout set "ct-timeout-1"'] ] self.verify_nftables(nftables_search, 'ip vyos_conntrack') self.verify_nftables(nftables6_search, 'ip6 vyos_conntrack') self.cli_delete(['firewall']) if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/src/conf_mode/system_conntrack.py b/src/conf_mode/system_conntrack.py index 2a55daed4..a1472aaaa 100755 --- a/src/conf_mode/system_conntrack.py +++ b/src/conf_mode/system_conntrack.py @@ -1,247 +1,252 @@ #!/usr/bin/env python3 # # Copyright (C) 2021-2023 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as # published by the Free Software Foundation. # # This program 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 # along with this program. If not, see <http://www.gnu.org/licenses/>. import os import re from sys import exit from vyos.config import Config from vyos.configdep import set_dependents, call_dependents from vyos.utils.process import process_named_running from vyos.utils.dict import dict_search from vyos.utils.dict import dict_search_args from vyos.utils.dict import dict_search_recursive from vyos.utils.process import cmd from vyos.utils.process import rc_cmd from vyos.utils.process import run from vyos.template import render from vyos import ConfigError from vyos import airbag airbag.enable() conntrack_config = r'/etc/modprobe.d/vyatta_nf_conntrack.conf' sysctl_file = r'/run/sysctl/10-vyos-conntrack.conf' nftables_ct_file = r'/run/nftables-ct.conf' # Every ALG (Application Layer Gateway) consists of either a Kernel Object # also called a Kernel Module/Driver or some rules present in iptables module_map = { 'ftp': { 'ko': ['nf_nat_ftp', 'nf_conntrack_ftp'], 'nftables': ['tcp dport {21} ct helper set "ftp_tcp" return'] }, 'h323': { 'ko': ['nf_nat_h323', 'nf_conntrack_h323'], 'nftables': ['udp dport {1719} ct helper set "ras_udp" return', 'tcp dport {1720} ct helper set "q931_tcp" return'] }, 'nfs': { 'nftables': ['tcp dport {111} ct helper set "rpc_tcp" return', 'udp dport {111} ct helper set "rpc_udp" return'] }, 'pptp': { 'ko': ['nf_nat_pptp', 'nf_conntrack_pptp'], 'nftables': ['tcp dport {1723} ct helper set "pptp_tcp" return'], 'ipv4': True }, + 'rtsp': { + 'ko': ['nf_nat_rtsp', 'nf_conntrack_rtsp'], + 'nftables': ['tcp dport {554} ct helper set "rtsp_tcp" return'], + 'ipv4': True + }, 'sip': { 'ko': ['nf_nat_sip', 'nf_conntrack_sip'], 'nftables': ['tcp dport {5060,5061} ct helper set "sip_tcp" return', 'udp dport {5060,5061} ct helper set "sip_udp" return'] }, 'sqlnet': { 'nftables': ['tcp dport {1521,1525,1536} ct helper set "tns_tcp" return'] }, 'tftp': { 'ko': ['nf_nat_tftp', 'nf_conntrack_tftp'], 'nftables': ['udp dport {69} ct helper set "tftp_udp" return'] }, } valid_groups = [ 'address_group', 'domain_group', 'network_group', 'port_group' ] def get_config(config=None): if config: conf = config else: conf = Config() base = ['system', 'conntrack'] conntrack = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True, with_recursive_defaults=True) conntrack['firewall'] = conf.get_config_dict(['firewall'], key_mangling=('-', '_'), get_first_key=True, no_tag_node_value_mangle=True) conntrack['ipv4_nat_action'] = 'accept' if conf.exists(['nat']) else 'return' conntrack['ipv6_nat_action'] = 'accept' if conf.exists(['nat66']) else 'return' conntrack['wlb_action'] = 'accept' if conf.exists(['load-balancing', 'wan']) else 'return' conntrack['wlb_local_action'] = conf.exists(['load-balancing', 'wan', 'enable-local-traffic']) conntrack['module_map'] = module_map if conf.exists(['service', 'conntrack-sync']): set_dependents('conntrack_sync', conf) # If conntrack status changes, VRF zone rules need updating if conf.exists(['vrf']): set_dependents('vrf', conf) return conntrack def verify(conntrack): for inet in ['ipv4', 'ipv6']: if dict_search_args(conntrack, 'ignore', inet, 'rule') != None: for rule, rule_config in conntrack['ignore'][inet]['rule'].items(): if dict_search('destination.port', rule_config) or \ dict_search('destination.group.port_group', rule_config) or \ dict_search('source.port', rule_config) or \ dict_search('source.group.port_group', rule_config): if 'protocol' not in rule_config or rule_config['protocol'] not in ['tcp', 'udp']: raise ConfigError(f'Port requires tcp or udp as protocol in rule {rule}') tcp_flags = dict_search_args(rule_config, 'tcp', 'flags') if tcp_flags: if dict_search_args(rule_config, 'protocol') != 'tcp': raise ConfigError('Protocol must be tcp when specifying tcp flags') not_flags = dict_search_args(rule_config, 'tcp', 'flags', 'not') if not_flags: duplicates = [flag for flag in tcp_flags if flag in not_flags] if duplicates: raise ConfigError(f'Cannot match a tcp flag as set and not set') for side in ['destination', 'source']: if side in rule_config: side_conf = rule_config[side] if 'group' in side_conf: if len({'address_group', 'network_group', 'domain_group'} & set(side_conf['group'])) > 1: raise ConfigError('Only one address-group, network-group or domain-group can be specified') for group in valid_groups: if group in side_conf['group']: group_name = side_conf['group'][group] error_group = group.replace("_", "-") if group in ['address_group', 'network_group', 'domain_group']: if 'address' in side_conf: raise ConfigError(f'{error_group} and address cannot both be defined') if group_name and group_name[0] == '!': group_name = group_name[1:] if inet == 'ipv6': group = f'ipv6_{group}' group_obj = dict_search_args(conntrack['firewall'], 'group', group, group_name) if group_obj is None: raise ConfigError(f'Invalid {error_group} "{group_name}" on ignore rule') if not group_obj: Warning(f'{error_group} "{group_name}" has no members!') if dict_search_args(conntrack, 'timeout', 'custom', inet, 'rule') != None: for rule, rule_config in conntrack['timeout']['custom'][inet]['rule'].items(): if 'protocol' not in rule_config: raise ConfigError(f'Conntrack custom timeout rule {rule} requires protocol tcp or udp') else: if 'tcp' in rule_config['protocol'] and 'udp' in rule_config['protocol']: raise ConfigError(f'conntrack custom timeout rule {rule} - Cant use both tcp and udp protocol') return None def generate(conntrack): if not os.path.exists(nftables_ct_file): conntrack['first_install'] = True # Determine if conntrack is needed conntrack['ipv4_firewall_action'] = 'return' conntrack['ipv6_firewall_action'] = 'return' for rules, path in dict_search_recursive(conntrack['firewall'], 'rule'): if any(('state' in rule_conf or 'connection_status' in rule_conf or 'offload_target' in rule_conf) for rule_conf in rules.values()): if path[0] == 'ipv4': conntrack['ipv4_firewall_action'] = 'accept' elif path[0] == 'ipv6': conntrack['ipv6_firewall_action'] = 'accept' render(conntrack_config, 'conntrack/vyos_nf_conntrack.conf.j2', conntrack) render(sysctl_file, 'conntrack/sysctl.conf.j2', conntrack) render(nftables_ct_file, 'conntrack/nftables-ct.j2', conntrack) return None def apply(conntrack): # Depending on the enable/disable state of the ALG (Application Layer Gateway) # modules we need to either insmod or rmmod the helpers. - + add_modules = [] rm_modules = [] for module, module_config in module_map.items(): if dict_search_args(conntrack, 'modules', module) is None: if 'ko' in module_config: unloaded = [mod for mod in module_config['ko'] if os.path.exists(f'/sys/module/{mod}')] rm_modules.extend(unloaded) else: if 'ko' in module_config: add_modules.extend(module_config['ko']) # Add modules before nftables uses them if add_modules: module_str = ' '.join(add_modules) cmd(f'modprobe -a {module_str}') # Load new nftables ruleset install_result, output = rc_cmd(f'nft -f {nftables_ct_file}') if install_result == 1: raise ConfigError(f'Failed to apply configuration: {output}') # Remove modules after nftables stops using them if rm_modules: module_str = ' '.join(rm_modules) cmd(f'rmmod {module_str}') try: call_dependents() except ConfigError: # Ignore config errors on dependent due to being called too early. Example: # ConfigError("ConfigError('Interface ethN requires an IP address!')") pass # We silently ignore all errors # See: https://bugzilla.redhat.com/show_bug.cgi?id=1264080 cmd(f'sysctl -f {sysctl_file}') return None if __name__ == '__main__': try: c = get_config() verify(c) generate(c) apply(c) except ConfigError as e: print(e) exit(1)