summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorViacheslav Hletenko <v.gletenko@vyos.io>2023-09-20 11:46:42 +0000
committerViacheslav Hletenko <v.gletenko@vyos.io>2023-09-21 12:30:39 +0000
commitbdad4e046872e054ec7783b2f04b73a8a690a045 (patch)
tree43a0c14ba06ed9ef6fa22b0150b686bd0f14621f
parentb52cf1b7b3bc138b26eb21f917967748c40f9d3a (diff)
downloadvyos-1x-bdad4e046872e054ec7783b2f04b73a8a690a045.tar.gz
vyos-1x-bdad4e046872e054ec7783b2f04b73a8a690a045.zip
T5217: Add firewall synproxy
Add ability to SYNPROXY connections It is useful to protect against TCP SYN flood attacks and port-scanners set firewall global-options syn-cookies 'enable' set firewall ipv4 input filter rule 10 action 'synproxy' set firewall ipv4 input filter rule 10 destination port '22' set firewall ipv4 input filter rule 10 inbound-interface interface-name 'eth1' set firewall ipv4 input filter rule 10 protocol 'tcp' set firewall ipv4 input filter rule 10 synproxy tcp mss '1460' set firewall ipv4 input filter rule 10 synproxy tcp window-scale '7'
-rw-r--r--interface-definitions/include/firewall/action-forward.xml.i8
-rw-r--r--interface-definitions/include/firewall/action.xml.i8
-rw-r--r--interface-definitions/include/firewall/common-rule-inet.xml.i3
-rw-r--r--interface-definitions/include/firewall/synproxy.xml.i40
-rw-r--r--python/vyos/firewall.py14
-rwxr-xr-xsmoketest/scripts/cli/test_firewall.py27
-rwxr-xr-xsrc/conf_mode/firewall.py10
7 files changed, 103 insertions, 7 deletions
diff --git a/interface-definitions/include/firewall/action-forward.xml.i b/interface-definitions/include/firewall/action-forward.xml.i
index f61e51887..4e59f3c6f 100644
--- a/interface-definitions/include/firewall/action-forward.xml.i
+++ b/interface-definitions/include/firewall/action-forward.xml.i
@@ -3,7 +3,7 @@
<properties>
<help>Rule action</help>
<completionHelp>
- <list>accept continue jump reject return drop queue offload</list>
+ <list>accept continue jump reject return drop queue offload synproxy</list>
</completionHelp>
<valueHelp>
<format>accept</format>
@@ -37,8 +37,12 @@
<format>offload</format>
<description>Offload packet via flowtable</description>
</valueHelp>
+ <valueHelp>
+ <format>synproxy</format>
+ <description>Synproxy connections</description>
+ </valueHelp>
<constraint>
- <regex>(accept|continue|jump|reject|return|drop|queue|offload)</regex>
+ <regex>(accept|continue|jump|reject|return|drop|queue|offload|synproxy)</regex>
</constraint>
</properties>
</leafNode>
diff --git a/interface-definitions/include/firewall/action.xml.i b/interface-definitions/include/firewall/action.xml.i
index 9391a7bee..954e4f23e 100644
--- a/interface-definitions/include/firewall/action.xml.i
+++ b/interface-definitions/include/firewall/action.xml.i
@@ -3,7 +3,7 @@
<properties>
<help>Rule action</help>
<completionHelp>
- <list>accept continue jump reject return drop queue</list>
+ <list>accept continue jump reject return drop queue synproxy</list>
</completionHelp>
<valueHelp>
<format>accept</format>
@@ -33,8 +33,12 @@
<format>queue</format>
<description>Enqueue packet to userspace</description>
</valueHelp>
+ <valueHelp>
+ <format>synproxy</format>
+ <description>Synproxy connections</description>
+ </valueHelp>
<constraint>
- <regex>(accept|continue|jump|reject|return|drop|queue)</regex>
+ <regex>(accept|continue|jump|reject|return|drop|queue|synproxy)</regex>
</constraint>
</properties>
</leafNode>
diff --git a/interface-definitions/include/firewall/common-rule-inet.xml.i b/interface-definitions/include/firewall/common-rule-inet.xml.i
index e51dd0056..b04e40fa0 100644
--- a/interface-definitions/include/firewall/common-rule-inet.xml.i
+++ b/interface-definitions/include/firewall/common-rule-inet.xml.i
@@ -219,6 +219,7 @@
</leafNode>
</children>
</node>
+#include <include/firewall/synproxy.xml.i>
<node name="state">
<properties>
<help>Session state</help>
@@ -372,4 +373,4 @@
</leafNode>
</children>
</node>
-<!-- include end --> \ No newline at end of file
+<!-- include end -->
diff --git a/interface-definitions/include/firewall/synproxy.xml.i b/interface-definitions/include/firewall/synproxy.xml.i
new file mode 100644
index 000000000..a65126ea9
--- /dev/null
+++ b/interface-definitions/include/firewall/synproxy.xml.i
@@ -0,0 +1,40 @@
+<!-- include start from firewall/synproxy.xml.i -->
+<node name="synproxy">
+ <properties>
+ <help>Synproxy options</help>
+ </properties>
+ <children>
+ <node name="tcp">
+ <properties>
+ <help>TCP synproxy options</help>
+ </properties>
+ <children>
+ <leafNode name="mss">
+ <properties>
+ <help>TCP Maximum segment size</help>
+ <valueHelp>
+ <format>u32:501-65535</format>
+ <description>Maximum segment size for synproxy connections</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 501-65535"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="window-scale">
+ <properties>
+ <help>TCP window scale for synproxy connections</help>
+ <valueHelp>
+ <format>u32:1-14</format>
+ <description>TCP window scale</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-14"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+</node>
+<!-- include end -->
diff --git a/python/vyos/firewall.py b/python/vyos/firewall.py
index 3ca7a25b9..1ec034937 100644
--- a/python/vyos/firewall.py
+++ b/python/vyos/firewall.py
@@ -249,6 +249,10 @@ def parse_rule(rule_conf, hook, fw_name, rule_id, ip_name):
output.append(f'{proto} {prefix}port {operator} @P_{group_name}')
+ if rule_conf['action'] == 'synproxy':
+ if 'synproxy' in rule_conf:
+ output.append('ct state invalid,untracked')
+
if 'hop_limit' in rule_conf:
operators = {'eq': '==', 'gt': '>', 'lt': '<'}
for op, operator in operators.items():
@@ -419,6 +423,16 @@ def parse_rule(rule_conf, hook, fw_name, rule_id, ip_name):
if 'queue_options' in rule_conf:
queue_opts = ','.join(rule_conf['queue_options'])
output.append(f'{queue_opts}')
+
+ # Synproxy
+ if 'synproxy' in rule_conf:
+ synproxy_mss = dict_search_args(rule_conf, 'synproxy', 'tcp', 'mss')
+ if synproxy_mss:
+ output.append(f'mss {synproxy_mss}')
+ synproxy_ws = dict_search_args(rule_conf, 'synproxy', 'tcp', 'window_scale')
+ if synproxy_ws:
+ output.append(f'wscale {synproxy_ws} timestamp sack-perm')
+
else:
output.append('return')
diff --git a/smoketest/scripts/cli/test_firewall.py b/smoketest/scripts/cli/test_firewall.py
index 676be5305..7fd13d92a 100755
--- a/smoketest/scripts/cli/test_firewall.py
+++ b/smoketest/scripts/cli/test_firewall.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021-2022 VyOS maintainers and contributors
+# 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
@@ -338,6 +338,31 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.verify_nftables(nftables_search, 'ip vyos_filter')
+ def test_ipv4_synproxy(self):
+ tcp_mss = '1460'
+ tcp_wscale = '7'
+ dport = '22'
+
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '10', 'action', 'drop'])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '10', 'protocol', 'tcp'])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '10', 'destination', 'port', dport])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '10', 'synproxy', 'tcp', 'mss', tcp_mss])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '10', 'synproxy', 'tcp', 'window-scale', tcp_wscale])
+
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '10', 'action', 'synproxy'])
+
+ self.cli_commit()
+
+ nftables_search = [
+ [f'tcp dport {dport} ct state invalid,untracked', f'synproxy mss {tcp_mss} wscale {tcp_wscale} timestamp sack-perm']
+ ]
+
+ self.verify_nftables(nftables_search, 'ip vyos_filter')
+
+
def test_ipv4_mask(self):
name = 'smoketest-mask'
interface = 'eth0'
diff --git a/src/conf_mode/firewall.py b/src/conf_mode/firewall.py
index 3d799318e..2ca4bbe2d 100755
--- a/src/conf_mode/firewall.py
+++ b/src/conf_mode/firewall.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021-2022 VyOS maintainers and contributors
+# 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
@@ -173,6 +173,14 @@ def verify_rule(firewall, rule_conf, ipv6):
if not dict_search_args(firewall, 'flowtable', offload_target):
raise ConfigError(f'Invalid offload-target. Flowtable "{offload_target}" does not exist on the system')
+ if rule_conf['action'] != 'synproxy' and 'synproxy' in rule_conf:
+ raise ConfigError('"synproxy" option allowed only for action synproxy')
+ if rule_conf['action'] == 'synproxy':
+ if not rule_conf.get('synproxy', {}).get('tcp'):
+ raise ConfigError('synproxy TCP MSS is not defined')
+ if rule_conf.get('protocol', {}) != 'tcp':
+ raise ConfigError('For action "synproxy" the protocol must be set to TCP')
+
if 'queue_options' in rule_conf:
if 'queue' not in rule_conf['action']:
raise ConfigError('queue-options defined, but action queue needed and it is not defined')