Firewall
Contents
Firewall#
Use iptables to block IP addresses by country for inbound ports on a server. This is a whitelist: what is not explicitly allowed is blocked. This is useful, for example, to filter most SSH bruteforce attacks while leaving a webserver freely available. This is certainly not the most efficient approach at filtering because you can easily end with thousands of iptables rules that need to be scanned one by one.
See also
A collection of scripts I have written and/or adapted that I currently use on my systems as automated tasks 1
Simple shell script for GNU/Linux, built on iptables, which is able to filter incoming packets based on accepted port numbers and countries. It is aimed to SOHO users. 2
Linux Iptables Just Block By Country - nixCraft 3
Simple stateful firewall - ArchWiki 4
iptables - ArchWiki 5
25 Most Frequently Used Linux IPTables Rules Examples 6
Setup#
install the dependencies
apt-get install iptables python3-yaml python3-requests iptables-persistent
answer
Yes
to all questionsinstall fpyutils. See reference
create the jobs directories. See reference
mkdir -p /home/jobs/{scripts,services}/by-user/root
create the
script
/home/jobs/scripts/by-user/root/iptables_geoport.py#1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3# 4# iptables_geoport.py 5# 6# Copyright (C) 2020-2022 Franco Masotti (franco \D\o\T masotti {-A-T-} tutanota \D\o\T com) 7# 8# This program is free software; you can redistribute it and/or modify 9# it under the terms of the GNU General Public License as published by 10# the Free Software Foundation; either version 2 of the License, or 11# (at your option) any later version. 12# 13# This program is distributed in the hope that it will be useful, 14# but WITHOUT ANY WARRANTY; without even the implied warranty of 15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16# GNU General Public License for more details. 17# 18# You should have received a copy of the GNU General Public License along 19# with this program; if not, write to the Free Software Foundation, Inc., 20# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 22# OLD NOTICES 23# =========== 24# See url for more info - http://www.cyberciti.biz/faq/?p=3402 25# Author: nixCraft <www.cyberciti.biz> under GPL v.2.0+ 26# Post Author: frnmst (Franco Masotti) (franco \D\o\T masotti {-A-T-} tutanota \D\o\T com) 27# New version heavily based on https://wiki.archlinux.org/index.php/Simple_stateful_firewall 28# https://wiki.archlinux.org/index.php/Iptables 29# and a little on http://www.thegeekstuff.com/2011/06/iptables-rules-examples/ as well as nixCraft for the bash stuff. 30# =========== 31r"""iptables_geoport.py.""" 32 33import copy 34import ipaddress 35import os 36import pathlib 37import shlex 38import sys 39import urllib 40 41import fpyutils 42import requests 43import yaml 44 45 46class UserNotRoot(Exception): 47 r"""The user running the script is not root.""" 48 49 50################## 51# Basic commands # 52################## 53 54 55def reset_rules(): 56 r"""Reset the chains and the tables.""" 57 # https://wiki.archlinux.org/index.php/Iptables#Resetting_rules 58 # 59 # Copyright (C) 2020 Arch Wiki contributors. 60 # Permission is granted to copy, distribute and/or modify this document 61 # under the terms of the GNU Free Documentation License, Version 1.3 62 # or any later version published by the Free Software Foundation; 63 # with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. 64 # A copy of the license is included in the section entitled "GNU 65 # Free Documentation License". 66 commands = dict() 67 commands['flush_tcp_chain'] = 'iptables --flush TCP' 68 commands['flush_udp_chain'] = 'iptables --flush UDP' 69 commands['flush_input_chain'] = 'iptables --flush INPUT' 70 commands['flush_output_chain'] = 'iptables --flush OUTPUT' 71 commands['flush_logging_chain'] = 'iptables --flush LOGGING' 72 73 commands['delete_tcp_chain'] = 'iptables --delete-chain TCP' 74 commands['delete_udp_chain'] = 'iptables --delete-chain UDP' 75 commands['delete_logging_chain'] = 'iptables --delete-chain LOGGING' 76 77 commands['flush_mangle_table'] = 'iptables --table mangle --flush' 78 commands['delete_mangle_chain'] = 'iptables --table mangle --delete-chain' 79 commands['flush_raw_table'] = 'iptables --table raw --flush' 80 commands['delete_raw_chain'] = 'iptables --table raw --delete-chain' 81 commands['flush_security_table'] = 'iptables --table security --flush' 82 commands[ 83 'delete_security_chain'] = 'iptables --table security --delete-chain' 84 commands['accept_input'] = 'iptables --policy INPUT ACCEPT' 85 commands['accept_forward'] = 'iptables --policy FORWARD ACCEPT' 86 commands['accept_output'] = 'iptables --policy OUTPUT ACCEPT' 87 88 # sys._getframe().f_code.co_name is the function name. 89 fix_dict_keys(commands, sys._getframe().f_code.co_name, '__') 90 return commands 91 92 93def initialize_basic_chains() -> dict: 94 r"""Apply some basic rules for a single machine.""" 95 # https://wiki.archlinux.org/index.php/Simple_stateful_firewall#Firewall_for_a_single_machine 96 # 97 # Copyright (C) 2020 Arch Wiki contributors. 98 # Permission is granted to copy, distribute and/or modify this document 99 # under the terms of the GNU Free Documentation License, Version 1.3 100 # or any later version published by the Free Software Foundation; 101 # with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. 102 # A copy of the license is included in the section entitled "GNU 103 # Free Documentation License". 104 commands = dict() 105 106 # Output traffic is NOT filtered. 107 commands['output_chain'] = 'iptables --policy OUTPUT ACCEPT' 108 109 # Create two user defined chains that will define tcp an udp protocol rules later. 110 commands['tcp_chain'] = 'iptables --new-chain TCP' 111 commands['udp_chain'] = 'iptables --new-chain UDP' 112 113 # For a single machine, however, we simply set the policy of the FORWARD chain to DROP and move on 114 commands['drop_forward'] = 'iptables --policy FORWARD DROP' 115 116 # The first rule added to the INPUT chain will allow traffic that belongs 117 # to established connections, or new valid traffic that is related to these 118 # connections such as ICMP errors, or echo replies. 119 commands[ 120 'allow_realted_established'] = 'iptables --append INPUT --match conntrack --ctstate RELATED,ESTABLISHED --jump ACCEPT' 121 122 # loopback interface INPUT traffic enabled for ping and debugging stuff. 123 commands[ 124 'loopback'] = 'iptables --append INPUT --in-interface lo --jump ACCEPT' 125 126 # Drop all invalid INPUT (i.e. damaged) packets. 127 # To do this connection must be tracked (conntrack) 128 # and connection state (cstate) is set to INVALID. 129 commands[ 130 'invalid_input'] = 'iptables --append INPUT --match conntrack --ctstate INVALID --jump DROP' 131 132 # Allow icmp type 8 (i.e. ping) to all interfaces. 133 commands[ 134 'ping'] = 'iptables --append INPUT --protocol icmp --icmp-type 8 --match conntrack --ctstate NEW --jump ACCEPT' 135 136 # TCP snd UDP chains are connected to INPUT chains. 137 # These two user-defined chains will manage the ports. 138 # Remember that tcp uses SYN to initialize a connection, unlike UDP 139 commands[ 140 'connect_tcp_chain'] = 'iptables --append INPUT --protocol tcp --syn -m conntrack --ctstate NEW --jump TCP' 141 commands[ 142 'connect_udp_chain'] = 'iptables --append INPUT --protocol udp -m conntrack --ctstate NEW --jump UDP' 143 144 fix_dict_keys(commands, sys._getframe().f_code.co_name, '__') 145 return commands 146 147 148def initialize_logging_chain() -> dict: 149 r"""Create the logging chain.""" 150 # https://wiki.archlinux.org/index.php/Iptables#Logging 151 # 152 # Copyright (C) 2020 Arch Wiki contributors. 153 # Permission is granted to copy, distribute and/or modify this document 154 # under the terms of the GNU Free Documentation License, Version 1.3 155 # or any later version published by the Free Software Foundation; 156 # with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. 157 # A copy of the license is included in the section entitled "GNU 158 # Free Documentation License". 159 commands = dict() 160 161 commands['logging_chain'] = 'iptables --new-chain LOGGING' 162 commands[ 163 'connect_logging_chain'] = 'iptables --append INPUT --jump LOGGING' 164 commands[ 165 'logging_limit'] = 'iptables --append LOGGING --match limit --limit 2/hour --limit-burst 10 --jump LOG' 166 167 fix_dict_keys(commands, sys._getframe().f_code.co_name, '__') 168 return commands 169 170 171def initialize_blocking_rules(drop_packets: bool = True, 172 logging: bool = True) -> dict: 173 r"""Initialize blocking rules.""" 174 # https://wiki.archlinux.org/index.php/Simple_stateful_firewall#Firewall_for_a_single_machine 175 # 176 # Copyright (C) 2020 Arch Wiki contributors. 177 # Permission is granted to copy, distribute and/or modify this document 178 # under the terms of the GNU Free Documentation License, Version 1.3 179 # or any later version published by the Free Software Foundation; 180 # with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. 181 # A copy of the license is included in the section entitled "GNU 182 # Free Documentation License". 183 commands = dict() 184 185 if logging: 186 chain = 'LOGGING' 187 else: 188 chain = 'INPUT' 189 if drop_packets: 190 # Drop everything. 191 commands['drop'] = 'iptables --append ' + chain + ' --jump DROP' 192 else: 193 # RFC compilant. 194 commands[ 195 'rfc_tcp'] = 'iptables --append ' + chain + ' --protocol tcp --jump REJECT --reject-with tcp-rst' 196 commands[ 197 'rfc_udp'] = 'iptables --append ' + chain + ' --protocol udp --jump REJECT --reject-with icmp-port-unreachable' 198 # Other protocols are usually not used, so REJECT those packets with icmp-proto-unreachable. 199 commands[ 200 'proto_unreachable'] = 'iptables --append ' + chain + ' --jump REJECT --reject-with icmp-proto-unreachable' 201 202 fix_dict_keys(commands, sys._getframe().f_code.co_name, '__') 203 return commands 204 205 206def set_accepted_rules(ports: dict, accepted_ips: dict) -> dict: 207 r"""Set accepted rules.""" 208 assert_input_ports_struct(ports) 209 assert_accepted_ips_struct(accepted_ips) 210 211 commands = dict() 212 # O(ports*chains*ips) <= O(ips^3) because of how this script works. 213 i = 0 214 for port in ports: 215 source = ports[port]['source'] 216 ips = accepted_ips[source] 217 chains, protocols = get_chains_and_protocols(ports[port]['protocol']) 218 for w, chain in enumerate(chains): 219 for ip in ips: 220 commands[str(i)] = generate_accepted_rule_command( 221 chain, protocols[w], port, ip) 222 i += 1 223 224 fix_dict_keys(commands, sys._getframe().f_code.co_name, '__') 225 return commands 226 227 228def set_patch_rules(rules: list) -> dict: 229 r"""Pass raw commands directly.""" 230 for r in rules: 231 if not isinstance(r, str): 232 raise TypeError 233 234 commands = dict() 235 for i, rule in enumerate(rules): 236 commands[str(i)] = rule 237 238 fix_dict_keys(commands, sys._getframe().f_code.co_name, '__') 239 return commands 240 241 242def initialize_drop_rules() -> dict: 243 r"""Initialize drop rules.""" 244 commands = dict() 245 commands['drop'] = 'iptables --policy INPUT DROP' 246 247 fix_dict_keys(commands, sys._getframe().f_code.co_name, '__') 248 return commands 249 250 251######### 252# Utils # 253######### 254 255 256def get_chains_and_protocols(protocol: str) -> tuple: 257 r"""Compute the iptables chain and protocol values.""" 258 if protocol not in ['tcp', 'udp', 'all']: 259 raise ValueError 260 261 chains = list() 262 protocols = list() 263 if protocol == 'tcp': 264 chains.append('TCP') 265 protocols.append('tcp') 266 elif protocol == 'udp': 267 chains.append('UDP') 268 protocols.append('udp') 269 elif protocol == 'all': 270 chains.append('TCP') 271 chains.append('UDP') 272 protocols.append('tcp') 273 protocols.append('udp') 274 275 return chains, protocols 276 277 278def generate_accepted_rule_command(chain: str, protocol: str, port: str, 279 remote_ip: str) -> str: 280 r"""Generate a single command for the accepted rules.""" 281 check_port(port) 282 check_ip_address(remote_ip) 283 284 return ('iptables --append ' + chain + ' --protocol ' + protocol + 285 ' --dport ' + port + ' --source ' + remote_ip + ' --jump ACCEPT') 286 287 288def fix_dict_keys(dictionary: dict, prefix: str, separator: str): 289 r"""Fix the keys of a dictionary by adding a prefix and separator.""" 290 d = copy.deepcopy(dictionary) 291 for key in d: 292 dictionary[prefix + separator + key] = d[key] 293 del dictionary[key] 294 295 296def load_zone_file(zone_file: str) -> list: 297 r"""Load zone file.""" 298 zones = list() 299 with open(zone_file, 'r') as f: 300 line = f.readline().strip() 301 while line: 302 check_ip_address(line) 303 zones.append(line) 304 line = f.readline().strip() 305 306 return zones 307 308 309def load_zone_files(zone_files: list) -> list: 310 r"""Load all the zone files content in a flat data structure.""" 311 for zf in zone_files: 312 if not isinstance(zf, str): 313 raise TypeError 314 315 zones = list() 316 for zf in zone_files: 317 zones += load_zone_file(zf) 318 319 return zones 320 321 322def get_filename_from_url(url: str) -> str: 323 r"""Use some tricks to get the filemame from a URL.""" 324 return pathlib.PurePath(urllib.parse.urlparse(url).path).name 325 326 327def download_zone_file(url: str, dst_directory: str) -> str: 328 r"""Save the zone file.""" 329 filename = get_filename_from_url(url) 330 pathlib.Path(dst_directory).mkdir(mode=0o700, parents=True, exist_ok=True) 331 full_path = str(pathlib.Path(dst_directory, filename)) 332 try: 333 r = requests.get(url) 334 with open(full_path, 'w') as f: 335 f.write(r.text) 336 except requests.ConnectionError: 337 pass 338 339 return full_path 340 341 342def download_zone_files(urls: list, cache_directory: str) -> list: 343 r"""Download multiple zone files.""" 344 for u in urls: 345 if not isinstance(u, str): 346 raise TypeError 347 348 files = list() 349 for u in urls: 350 files.append(download_zone_file(u, cache_directory)) 351 352 return files 353 354 355def update_accepted_ips_structure(accepted_ips: dict, zones: list): 356 r"""Update some data structures.""" 357 assert_accepted_ips_struct(accepted_ips) 358 assert_zones_struct(zones) 359 360 accepted_ips['wan'] = zones 361 accepted_ips['all'] = accepted_ips['lan'] + accepted_ips['wan'] 362 363 364def get_packet_policy(invalid_packet_policy: str) -> bool: 365 r"""Update a variable.""" 366 if invalid_packet_policy not in ['polite', 'rude']: 367 raise ValueError 368 369 drop = True 370 if invalid_packet_policy == 'rude': 371 drop = True 372 elif invalid_packet_policy == 'polite': 373 drop = False 374 return drop 375 376 377############## 378# Assertions # 379############## 380 381 382def check_ip_address(ip: str): 383 r"""Verify that we are dealing with a network address.""" 384 ipaddress.ip_network(ip, strict=True) 385 386 387def check_port(port: str): 388 r"""Check that the input port is a valid port number.""" 389 if not port.isdigit(): 390 raise TypeError 391 if not (int(port) >= 0 and int(port) <= (2**16) - 1): 392 raise ValueError 393 394 395def assert_zones_struct(zones: list): 396 r"""Check that the data structure is a list of ip addresses.""" 397 for z in zones: 398 if not isinstance(z, str): 399 raise TypeError 400 check_ip_address(z) 401 402 403def assert_accepted_ips_struct(accepted_ips: dict): 404 r"""Check that the data structure is a list of ip addresses.""" 405 for ips in accepted_ips: 406 if not isinstance(accepted_ips[ips], list): 407 raise TypeError 408 for j in accepted_ips[ips]: 409 if not isinstance(j, str): 410 raise TypeError 411 check_ip_address(j) 412 413 414def assert_input_ports_struct(ports: dict): 415 r"""Check that the data structure is correct.""" 416 for port in ports: 417 check_port(port) 418 if not isinstance(ports[port], dict): 419 raise TypeError 420 if 'source' not in ports[port]: 421 raise ValueError 422 if 'protocol' not in ports[port]: 423 raise ValueError 424 if ports[port]['source'] not in ['lan', 'wan', 'all']: 425 raise ValueError 426 427 428############ 429# Pipeline # 430############ 431 432if __name__ == '__main__': 433 if os.getuid() != 0: 434 raise UserNotRoot 435 436 # Load the configuration. 437 configuration_file = shlex.quote(sys.argv[1]) 438 config = yaml.load(open(configuration_file, 'r'), Loader=yaml.SafeLoader) 439 dry_run = config['dry_run'] 440 cache_directory = config['cache_directory'] 441 zone_files = config['accepted_ips']['wan'] 442 accepted_ips = dict() 443 accepted_ips['lan'] = config['accepted_ips']['lan'] 444 accepted_ips['wan'] = list() 445 patch_rules = config['patch_rules'] 446 set_patch_rules_first = config['set_patch_rules_first'] 447 input_ports = config['input_ports'] 448 logging = config['logging_enabled'] 449 invalid_packet_policy = config['invalid_packet_policy'] 450 drop_packets = get_packet_policy(invalid_packet_policy) 451 452 # Get the data. 453 zones = load_zone_files(download_zone_files(zone_files, cache_directory)) 454 update_accepted_ips_structure(accepted_ips, zones) 455 456 # Apply the rules. 457 reset = reset_rules() 458 basic_chains = initialize_basic_chains() 459 logging_chain = initialize_logging_chain() 460 blocking_rules = initialize_blocking_rules(drop_packets, logging) 461 rules = set_accepted_rules(input_ports, accepted_ips) 462 patch = set_patch_rules(patch_rules) 463 drop_by_default = initialize_drop_rules() 464 465 # Merge the rules. 466 if set_patch_rules_first: 467 commands = { 468 **reset, 469 **basic_chains, 470 **logging_chain, 471 **blocking_rules, 472 **patch, 473 **rules, 474 **drop_by_default 475 } 476 else: 477 commands = { 478 **reset, 479 **basic_chains, 480 **logging_chain, 481 **blocking_rules, 482 **rules, 483 **patch, 484 **drop_by_default 485 } 486 487 # Apply the rules. 488 for c in commands: 489 fpyutils.shell.execute_command_live_output(commands[c], 490 dry_run=dry_run)
create a
configuration file
/home/jobs/scripts/by-user/root/iptables_geoport.yaml#1# 2# iptables_geoport.yaml 3# 4# Copyright (C) 2020-2022 Franco Masotti (franco \D\o\T masotti {-A-T-} tutanota \D\o\T com) 5# 6# This program is free software; you can redistribute it and/or modify 7# it under the terms of the GNU General Public License as published by 8# the Free Software Foundation; either version 2 of the License, or 9# (at your option) any later version. 10# 11# This program is distributed in the hope that it will be useful, 12# but WITHOUT ANY WARRANTY; without even the implied warranty of 13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14# GNU General Public License for more details. 15# 16# You should have received a copy of the GNU General Public License along 17# with this program; if not, write to the Free Software Foundation, Inc., 18# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 20# If set to true print the commands that would be executed. 21dry_run: true 22 23logging_enabled: true 24 25# {rude,polite} 26invalid_packet_policy: 'rude' 27 28# The path where the ip addresses list will be saved. 29cache_directory: './.cache' 30 31# Have a look at https://www.ipdeny.com/ipblocks/ 32accepted_ips: 33 wan: 34 - 'https://www.ipdeny.com/ipblocks/data/countries/it.zone' 35 lan: 36 - '192.168.0.0/24' 37 38# If set to 'true' the "patch rules" will be applied before the "input ports" rules. 39set_patch_rules_first: true 40 41# Raw rules that override the default ones. 42# Use "[]" if you do not need patch rules. 43patch_rules: 44 # Web server. 45 - 'iptables -A TCP -p tcp --dport 80 -j ACCEPT' 46 - 'iptables -A TCP -p tcp --dport 443 -j ACCEPT' 47 48 # SSH LAN only. 49 - 'iptables -A TCP -s 192.168.0.0/24 -p tcp -m tcp --dport 22 -j ACCEPT' 50 51 # SMTP. 52 - 'iptables -A TCP -p tcp --dport 465 -j ACCEPT' 53 54 # IMAP. 55 - 'iptables -A TCP -p tcp --dport 993 -j ACCEPT' 56 57 # Avahi 58 - 'iptables -A UDP -p udp -m udp --dport 5353 -j ACCEPT' 59 - 'iptables -A INPUT -p udp --dport 5353 -j ACCEPT' 60 - 'iptables -A OUTPUT -p udp --dport 5353 -j ACCEPT' 61 62 # KDE Connect. 63 - 'iptables -A UDP -p udp --dport 1714:1764 -j ACCEPT' 64 - 'iptables -A TCP -p tcp --dport 1714:1764 -j ACCEPT' 65 66# source: {lan,wan,all} 67# protocol: {tcp,udp,all} 68input_ports: 69 '2222': 70 source: 'lan' 71 protocol: 'tcp' 72 '2223': 73 source: 'lan' 74 protocol: 'tcp' 75 '5555': 76 source: 'lan' 77 protocol: 'tcp' 78 '53': 79 source: 'lan' 80 protocol: 'all' 81 82 # CUPS. 83 '631': 84 source: 'lan' 85 protocol: 'tcp' 86 87 '8100': 88 source: 'lan' 89 protocol: 'tcp' 90 91 # Required for SANE. 92 '6566': 93 source: 'lan' 94 protocol: 'all' 95 96 # Move SSH rules here for performance reasons. 97 '22': 98 source: 'wan' 99 protocol: 'tcp'
Note
Rules are scanned sequentially. Move frequently used rules upper in the file to improve performance.
use this
Systemd service unit file
/home/jobs/services/by-user/root/iptables-geoport.service#1[Unit] 2Description=Apply the iptables geoport rules 3Wants=network-online.target 4After=network-online.target 5Requires=netfilter-persistent.service 6After=netfilter-persistent.service 7 8[Service] 9Type=simple 10# Answer Yes (default value) to dpkg-reconfigure. 11ExecStart=/usr/bin/bash -c '/home/jobs/scripts/by-user/root/iptables_geoport.py /home/jobs/scripts/by-user/root/iptables_geoport.yaml && [ -f /sbin/dpkg-reconfigure ] && dpkg-reconfigure --frontend noninteractive iptables-persistent' 12 13User=root 14Group=root
fix the permissions
chmod 700 -R /home/jobs/scripts/by-user/iptables_geoport.* chmod 700 -R /home/jobs/services/by-user/root
run the deploy script
SANE#
To be able to use a remote scanner with SANE using this setup you need to follow these steps
open TCP and UDP ports 6566
/home/jobs/scripts/by-user/root/iptables_geoport.yaml (extract)#65# source: {lan,wan,all} 66# protocol: {tcp,udp,all} 67input_ports: 68 '2222': 69 source: 'lan' 70 protocol: 'tcp' 71 '2223': 72 source: 'lan' 73 protocol: 'tcp' 74 '5555': 75 source: 'lan' 76 protocol: 'tcp' 77 '53': 78 source: 'lan' 79 protocol: 'all' 80 81 # CUPS. 82 '631': 83 source: 'lan' 84 protocol: 'tcp' 85 86 '8100': 87 source: 'lan' 88 protocol: 'tcp' 89 90 # Required for SANE. 91 '6566': 92 source: 'lan' 93 protocol: 'all' 94 95 # Move SSH rules here for performance reasons. 96 '22': 97 source: 'wan' 98 protocol: 'tcp'
add
this file
to load the kernel module/etc/modprobe.d/nf_conntrack.conf#1options nf_conntrack nf_conntrack_helper=1
add
this file
to load the kernel module/etc/modules-load.d/nf_conntrack_sane.conf#1nf_conntrack_sane
reboot
check if the rules are active:
1
should be returned by thecat
commandcat /proc/sys/net/netfilter/nf_conntrack_helper 1
Footnotes
- 1
https://software.franco.net.eu.org/frnmst/automated-tasks GNU GPLv3+, copyright (c) 2019-2022, Franco Masotti
- 2
https://software.franco.net.eu.org/frnmst-archives/iptables-geoport-directives GNU GPLv3+ and GNU GPLv2+, copyright (c) 2015 Franco Masotti
- 3
http://www.cyberciti.biz/faq/?p=3402 GPL v.2.0+, copyright (c) Author: nixCraft <www.cyberciti.biz>
- 4
https://wiki.archlinux.org/index.php/Simple_stateful_firewall GNU Free Documentation License 1.3 or late, copyright (c) ArchWiki contributors
- 5
https://wiki.archlinux.org/index.php/Iptables GNU Free Documentation License 1.3 or late, copyright (c) ArchWiki contributors
- 6
http://www.thegeekstuff.com/2011/06/iptables-rules-examples/ unknown license
- 7
https://wiki.archlinux.org/title/SANE#Firewall GNU Free Documentation License 1.3 or late, copyright (c) ArchWiki contributors
- 8
https://home.regit.org/wp-content/uploads/2011/11/secure-conntrack-helpers.html unknown license