summaryrefslogtreecommitdiff
path: root/src/conf_mode
diff options
context:
space:
mode:
Diffstat (limited to 'src/conf_mode')
-rwxr-xr-xsrc/conf_mode/firewall.py43
-rwxr-xr-xsrc/conf_mode/interfaces_tunnel.py19
-rwxr-xr-xsrc/conf_mode/load-balancing_reverse-proxy.py13
-rwxr-xr-xsrc/conf_mode/nat64.py10
-rwxr-xr-xsrc/conf_mode/nat_cgnat.py71
-rwxr-xr-xsrc/conf_mode/protocols_bfd.py2
-rwxr-xr-xsrc/conf_mode/system_conntrack.py3
7 files changed, 100 insertions, 61 deletions
diff --git a/src/conf_mode/firewall.py b/src/conf_mode/firewall.py
index e96e57154..4c289b921 100755
--- a/src/conf_mode/firewall.py
+++ b/src/conf_mode/firewall.py
@@ -33,6 +33,7 @@ from vyos.template import render
from vyos.utils.dict import dict_search_args
from vyos.utils.dict import dict_search_recursive
from vyos.utils.process import call
+from vyos.utils.process import cmd
from vyos.utils.process import rc_cmd
from vyos import ConfigError
from vyos import airbag
@@ -40,20 +41,7 @@ from vyos import airbag
airbag.enable()
nftables_conf = '/run/nftables.conf'
-
-sysfs_config = {
- 'all_ping': {'sysfs': '/proc/sys/net/ipv4/icmp_echo_ignore_all', 'enable': '0', 'disable': '1'},
- 'broadcast_ping': {'sysfs': '/proc/sys/net/ipv4/icmp_echo_ignore_broadcasts', 'enable': '0', 'disable': '1'},
- 'directed_broadcast' : {'sysfs': '/proc/sys/net/ipv4/conf/all/bc_forwarding', 'enable': '1', 'disable': '0'},
- 'ip_src_route': {'sysfs': '/proc/sys/net/ipv4/conf/*/accept_source_route'},
- 'ipv6_receive_redirects': {'sysfs': '/proc/sys/net/ipv6/conf/*/accept_redirects'},
- 'ipv6_src_route': {'sysfs': '/proc/sys/net/ipv6/conf/*/accept_source_route', 'enable': '0', 'disable': '-1'},
- 'log_martians': {'sysfs': '/proc/sys/net/ipv4/conf/all/log_martians'},
- 'receive_redirects': {'sysfs': '/proc/sys/net/ipv4/conf/*/accept_redirects'},
- 'send_redirects': {'sysfs': '/proc/sys/net/ipv4/conf/*/send_redirects'},
- 'syn_cookies': {'sysfs': '/proc/sys/net/ipv4/tcp_syncookies'},
- 'twa_hazards_protection': {'sysfs': '/proc/sys/net/ipv4/tcp_rfc1337'}
-}
+sysctl_file = r'/run/sysctl/10-vyos-firewall.conf'
valid_groups = [
'address_group',
@@ -351,7 +339,7 @@ def verify(firewall):
verify_nested_group(group_name, group, groups, [])
if 'ipv4' in firewall:
- for name in ['name','forward','input','output']:
+ for name in ['name','forward','input','output', 'prerouting']:
if name in firewall['ipv4']:
for name_id, name_conf in firewall['ipv4'][name].items():
if 'jump' in name_conf['default_action'] and 'default_jump_target' not in name_conf:
@@ -371,7 +359,7 @@ def verify(firewall):
verify_rule(firewall, rule_conf, False)
if 'ipv6' in firewall:
- for name in ['name','forward','input','output']:
+ for name in ['name','forward','input','output', 'prerouting']:
if name in firewall['ipv6']:
for name_id, name_conf in firewall['ipv6'][name].items():
if 'jump' in name_conf['default_action'] and 'default_jump_target' not in name_conf:
@@ -467,33 +455,16 @@ def generate(firewall):
local_zone_conf['from_local'][zone] = zone_conf['from'][local_zone]
render(nftables_conf, 'firewall/nftables.j2', firewall)
+ render(sysctl_file, 'firewall/sysctl-firewall.conf.j2', firewall)
return None
-def apply_sysfs(firewall):
- for name, conf in sysfs_config.items():
- paths = glob(conf['sysfs'])
- value = None
-
- if name in firewall['global_options']:
- conf_value = firewall['global_options'][name]
- if conf_value in conf:
- value = conf[conf_value]
- elif conf_value == 'enable':
- value = '1'
- elif conf_value == 'disable':
- value = '0'
-
- if value:
- for path in paths:
- with open(path, 'w') as f:
- f.write(value)
-
def apply(firewall):
install_result, output = rc_cmd(f'nft --file {nftables_conf}')
if install_result == 1:
raise ConfigError(f'Failed to apply firewall: {output}')
- apply_sysfs(firewall)
+ # Apply firewall global-options sysctl settings
+ cmd(f'sysctl -f {sysctl_file}')
call_dependents()
diff --git a/src/conf_mode/interfaces_tunnel.py b/src/conf_mode/interfaces_tunnel.py
index 43ba72857..98ef98d12 100755
--- a/src/conf_mode/interfaces_tunnel.py
+++ b/src/conf_mode/interfaces_tunnel.py
@@ -145,11 +145,20 @@ def verify(tunnel):
# If no IP GRE key is defined we can not have more then one GRE tunnel
# bound to any one interface/IP address and the same remote. This will
# result in a OS PermissionError: add tunnel "gre0" failed: File exists
- if (their_address == our_address or our_source_if == their_source_if) and \
- our_remote == their_remote:
- raise ConfigError(f'Missing required "ip key" parameter when '\
- 'running more then one GRE based tunnel on the '\
- 'same source-interface/source-address')
+ if our_remote == their_remote:
+ if our_address is not None and their_address == our_address:
+ # If set to the same values, this is always a fail
+ raise ConfigError(f'Missing required "ip key" parameter when '\
+ 'running more then one GRE based tunnel on the '\
+ 'same source-address')
+
+ if their_source_if == our_source_if and their_address == our_address:
+ # Note that lack of None check on these is deliberate.
+ # source-if and source-ip matching while unset (all None) is a fail
+ # source-ifs set and matching with unset source-ips is a fail
+ raise ConfigError(f'Missing required "ip key" parameter when '\
+ 'running more then one GRE based tunnel on the '\
+ 'same source-interface')
# Keys are not allowed with ipip and sit tunnels
if tunnel['encapsulation'] in ['ipip', 'sit']:
diff --git a/src/conf_mode/load-balancing_reverse-proxy.py b/src/conf_mode/load-balancing_reverse-proxy.py
index 1c1252df0..09c68dadd 100755
--- a/src/conf_mode/load-balancing_reverse-proxy.py
+++ b/src/conf_mode/load-balancing_reverse-proxy.py
@@ -79,12 +79,21 @@ def verify(lb):
raise ConfigError(f'"TCP" port "{tmp_port}" is used by another service')
for back, back_config in lb['backend'].items():
- if 'http-check' in back_config:
- http_check = back_config['http-check']
+ if 'http_check' in back_config:
+ http_check = back_config['http_check']
if 'expect' in http_check and 'status' in http_check['expect'] and 'string' in http_check['expect']:
raise ConfigError(f'"expect status" and "expect string" can not be configured together!')
+
+ if 'health_check' in back_config:
+ if 'mode' not in back_config or back_config['mode'] != 'tcp':
+ raise ConfigError(f'backend "{back}" can only be configured with {back_config["health_check"]} ' +
+ f'health-check whilst in TCP mode!')
+ if 'http_check' in back_config:
+ raise ConfigError(f'backend "{back}" cannot be configured with both http-check and health-check!')
+
if 'server' not in back_config:
raise ConfigError(f'"{back} server" must be configured!')
+
for bk_server, bk_server_conf in back_config['server'].items():
if 'address' not in bk_server_conf or 'port' not in bk_server_conf:
raise ConfigError(f'"backend {back} server {bk_server} address and port" must be configured!')
diff --git a/src/conf_mode/nat64.py b/src/conf_mode/nat64.py
index c1e7ebf85..32a1c47d1 100755
--- a/src/conf_mode/nat64.py
+++ b/src/conf_mode/nat64.py
@@ -20,7 +20,7 @@ import csv
import os
import re
-from ipaddress import IPv6Network
+from ipaddress import IPv6Network, IPv6Address
from json import dumps as json_write
from vyos import ConfigError
@@ -103,8 +103,14 @@ def verify(nat64) -> None:
# Verify that source.prefix is set and is a /96
if not dict_search("source.prefix", instance):
raise ConfigError(f"Source NAT64 rule {rule} missing source prefix")
- if IPv6Network(instance["source"]["prefix"]).prefixlen != 96:
+ src_prefix = IPv6Network(instance["source"]["prefix"])
+ if src_prefix.prefixlen != 96:
raise ConfigError(f"Source NAT64 rule {rule} source prefix must be /96")
+ if (int(src_prefix[0]) & int(IPv6Address('0:0:0:0:ff00::'))) != 0:
+ raise ConfigError(
+ f'Source NAT64 rule {rule} source prefix is not RFC6052-compliant: '
+ 'bits 64 to 71 (9th octet) must be zeroed'
+ )
pools = dict_search("translation.pool", instance)
if pools:
diff --git a/src/conf_mode/nat_cgnat.py b/src/conf_mode/nat_cgnat.py
index 957b12c28..d429f6e21 100755
--- a/src/conf_mode/nat_cgnat.py
+++ b/src/conf_mode/nat_cgnat.py
@@ -111,7 +111,19 @@ def generate_port_rules(
port_count: int,
global_port_range: str = '1024-65535',
) -> list:
- """Generates list of nftables rules for the batch file."""
+ """Generates a list of nftables option rules for the batch file.
+
+ Args:
+ external_hosts (list): A list of external host IPs.
+ internal_hosts (list): A list of internal host IPs.
+ port_count (int): The number of ports required per host.
+ global_port_range (str): The global port range to be used. Default is '1024-65535'.
+
+ Returns:
+ list: A list containing two elements:
+ - proto_map_elements (list): A list of proto map elements.
+ - other_map_elements (list): A list of other map elements.
+ """
rules = []
proto_map_elements = []
other_map_elements = []
@@ -120,13 +132,6 @@ def generate_port_rules(
# Calculate the required number of ports per host
required_ports_per_host = port_count
-
- # Check if there are enough external addresses for all internal hosts
- if required_ports_per_host * len(internal_hosts) > total_possible_ports * len(
- external_hosts
- ):
- raise ConfigError("Not enough ports available for the specified parameters!")
-
current_port = start_port
current_external_index = 0
@@ -141,13 +146,6 @@ def generate_port_rules(
current_port = start_port
next_end_port = current_port + required_ports_per_host - 1
- # Ensure the same port is not assigned to the same external host
- if any(
- rule.endswith(f'{external_host}:{current_port}-{next_end_port}')
- for rule in rules
- ):
- raise ConfigError("Not enough ports available for the specified parameters")
-
proto_map_elements.append(
f'{internal_host} : {external_host} . {current_port}-{next_end_port}'
)
@@ -240,6 +238,49 @@ def verify(config):
used_external_pools[external_pool] = rule
used_internal_pools[internal_pool] = rule
+ # Check calculation for allocation
+ external_port_range: str = config['pool']['external'][external_pool]['external_port_range']
+
+ external_ip_ranges: list = list(
+ config['pool']['external'][external_pool]['range']
+ )
+ internal_ip_ranges: list = config['pool']['internal'][internal_pool]['range']
+ start_port, end_port = map(int, external_port_range.split('-'))
+ ports_per_range_count: int = (end_port - start_port) + 1
+
+ external_list_hosts_count = []
+ external_list_hosts = []
+ internal_list_hosts_count = []
+ internal_list_hosts = []
+ for ext_range in external_ip_ranges:
+ # External hosts count
+ e_count = IPOperations(ext_range).get_ips_count()
+ external_list_hosts_count.append(e_count)
+ # External hosts list
+ e_hosts = IPOperations(ext_range).convert_prefix_to_list_ips()
+ external_list_hosts.extend(e_hosts)
+ for int_range in internal_ip_ranges:
+ # Internal hosts count
+ i_count = IPOperations(int_range).get_ips_count()
+ internal_list_hosts_count.append(i_count)
+ # Internal hosts list
+ i_hosts = IPOperations(int_range).convert_prefix_to_list_ips()
+ internal_list_hosts.extend(i_hosts)
+
+ external_host_count = sum(external_list_hosts_count)
+ internal_host_count = sum(internal_list_hosts_count)
+ ports_per_user: int = int(
+ config['pool']['external'][external_pool]['per_user_limit']['port']
+ )
+ users_per_extip = ports_per_range_count // ports_per_user
+ max_users = users_per_extip * external_host_count
+
+ if internal_host_count > max_users:
+ raise ConfigError(
+ f'Rule "{rule}" does not have enough ports available for the '
+ f'specified parameters'
+ )
+
def generate(config):
if not config:
diff --git a/src/conf_mode/protocols_bfd.py b/src/conf_mode/protocols_bfd.py
index 1c01a9013..1361bb1a9 100755
--- a/src/conf_mode/protocols_bfd.py
+++ b/src/conf_mode/protocols_bfd.py
@@ -49,7 +49,7 @@ def verify(bfd):
for peer, peer_config in bfd['peer'].items():
# IPv6 link local peers require an explicit local address/interface
if is_ipv6_link_local(peer):
- if 'source' not in peer_config or len(peer_config['source'] < 2):
+ if 'source' not in peer_config or len(peer_config['source']) < 2:
raise ConfigError('BFD IPv6 link-local peers require explicit local address and interface setting')
# IPv6 peers require an explicit local address
diff --git a/src/conf_mode/system_conntrack.py b/src/conf_mode/system_conntrack.py
index 031fe63b0..aa290788c 100755
--- a/src/conf_mode/system_conntrack.py
+++ b/src/conf_mode/system_conntrack.py
@@ -18,6 +18,7 @@ import os
from sys import exit
+from vyos.base import Warning
from vyos.config import Config
from vyos.configdep import set_dependents, call_dependents
from vyos.utils.dict import dict_search
@@ -165,6 +166,8 @@ def verify(conntrack):
if not group_obj:
Warning(f'{error_group} "{group_name}" has no members!')
+ Warning(f'It is prefered to define {inet} conntrack ignore rules in <firewall {inet} prerouting raw> section')
+
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: