Role ipt-server: sync with ipt-server repo (93d9af8); adapt extract-fw-host-vars.py.

- Update firewall scripts and shared conf files from firewall/ipt-server
  (HTTP connection limits, IPv6 TCP MSS clamping, ban_ipv6_mask,
  mattermost TCP fallback, rename 'stansard_*' -> 'standard_*').
- New role variables: fw_set_max_new_http_conn_per_ip_per_second,
  fw_max_http_new_conn_per_ip_per_second, fw_set_max_concurrent_http_conn_per_ip,
  fw_max_concurrent_http_conn_per_ip, fw_set_max_new_http_conn_per_second,
  fw_max_new_http_conn_per_second, fw_throttle_tcp6_mss_for_https,
  fw_tcp6_mss_for_https_val, fw_mm_tcp_ports_in, fw_ban_ipv6_mask.
- Split fw_blocked_ips into fw_blocked_ips_v4 / fw_blocked_ips_v6 and
  rename it in host_vars accordingly.
- Restart firewall when scripts or shared conf files change.
- extract-fw-host-vars.py: map new variables, normalize '${var}' and renamed
  bash variables, fall back to IPv6 for shared variables and warn if IPv4
  and IPv6 values differ.
- README.md / MIGRATION.md: document host_vars naming
  (<short-hostname>-ipt-server.yml) and new variables.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-02 22:01:28 +01:00
co-authored by Claude Opus 5.5
parent 4a6fc84c01
commit 402a6c9e89
22 changed files with 666 additions and 55 deletions
+68 -12
View File
@@ -21,7 +21,7 @@ from pathlib import Path
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Defaults matching roles/ipt-firewall/defaults/main.yml # Defaults matching roles/ipt-server/defaults/main.yml
# Only values that differ from these will be emitted. # Only values that differ from these will be emitted.
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
DEFAULTS = { DEFAULTS = {
@@ -73,8 +73,19 @@ DEFAULTS = {
"fw_http_ports": "$standard_http_ports", "fw_http_ports": "$standard_http_ports",
"fw_log_cgi_traffic_out": False, "fw_log_cgi_traffic_out": False,
"fw_cgi_script_users": "", "fw_cgi_script_users": "",
"fw_set_max_new_http_conn_per_ip_per_second": True,
"fw_max_http_new_conn_per_ip_per_second": "$default_max_http_new_conn_per_ip_per_second",
"fw_set_max_concurrent_http_conn_per_ip": True,
"fw_max_concurrent_http_conn_per_ip": "$default_max_concurrent_http_conn_per_ip",
"fw_set_max_new_http_conn_per_second": True,
"fw_max_new_http_conn_per_second": "$default_max_new_http_conn_per_second",
"fw_throttle_tcp6_mss_for_https": False,
"fw_tcp6_mss_for_https_val": "$min_tcp6_mss_for_https_val",
"fw_mm_server_ips": "", "fw_mm_server_ips": "",
"fw_forward_mm_server_ips": "", "fw_forward_mm_server_ips": "",
"fw_mm_udp_ports_in": "$standard_mattermost_udp_ports_in",
"fw_mm_tcp_ports_in": "$standard_mattermost_tcp_ports_in",
"fw_mm_udp_ports_out": "$standard_mattermost_udp_ports_out",
"fw_smtpd_ips": "", "fw_smtpd_ips": "",
"fw_forward_smtpd_ips": "", "fw_forward_smtpd_ips": "",
"fw_smtpd_additional_listen_ports": "", "fw_smtpd_additional_listen_ports": "",
@@ -141,7 +152,9 @@ DEFAULTS = {
"fw_portforward_udp_v4": "", "fw_portforward_udp_v4": "",
"fw_portforward_tcp_v6": "", "fw_portforward_tcp_v6": "",
"fw_portforward_udp_v6": "", "fw_portforward_udp_v6": "",
"fw_blocked_ips": "", "fw_blocked_ips_v4": "",
"fw_blocked_ips_v6": "",
"fw_ban_ipv6_mask": "$default_ban_ipv6_mask",
"fw_block_tcp_ports": "111 113 135 137:139 445", "fw_block_tcp_ports": "111 113 135 137:139 445",
"fw_block_udp_ports": "111 137:139", "fw_block_udp_ports": "111 137:139",
"fw_create_traffic_counter": True, "fw_create_traffic_counter": True,
@@ -207,8 +220,17 @@ MAIN_SHARED = {
"http_ports": "fw_http_ports", "http_ports": "fw_http_ports",
"log_cgi_traffic_out": "fw_log_cgi_traffic_out", "log_cgi_traffic_out": "fw_log_cgi_traffic_out",
"cgi_script_users": "fw_cgi_script_users", "cgi_script_users": "fw_cgi_script_users",
"set_max_new_http_conn_per_ip_per_second": "fw_set_max_new_http_conn_per_ip_per_second",
"max_http_new_conn_per_ip_per_second": "fw_max_http_new_conn_per_ip_per_second",
"set_max_concurrent_http_conn_per_ip": "fw_set_max_concurrent_http_conn_per_ip",
"max_concurrent_http_conn_per_ip": "fw_max_concurrent_http_conn_per_ip",
"set_max_new_http_conn_per_second": "fw_set_max_new_http_conn_per_second",
"max_new_http_conn_per_second": "fw_max_new_http_conn_per_second",
"mm_server_ips": "fw_mm_server_ips", "mm_server_ips": "fw_mm_server_ips",
"forward_mm_server_ips": "fw_forward_mm_server_ips", "forward_mm_server_ips": "fw_forward_mm_server_ips",
"mm_udp_ports_in": "fw_mm_udp_ports_in",
"mm_tcp_ports_in": "fw_mm_tcp_ports_in",
"mm_udp_ports_out": "fw_mm_udp_ports_out",
"smtpd_ips": "fw_smtpd_ips", "smtpd_ips": "fw_smtpd_ips",
"forward_smtpd_ips": "fw_forward_smtpd_ips", "forward_smtpd_ips": "fw_forward_smtpd_ips",
"smtpd_additional_listen_ports": "fw_smtpd_additional_listen_ports", "smtpd_additional_listen_ports": "fw_smtpd_additional_listen_ports",
@@ -265,7 +287,6 @@ MAIN_SHARED = {
"forward_tcp_out_ports": "fw_forward_tcp_out_ports", "forward_tcp_out_ports": "fw_forward_tcp_out_ports",
"udp_out_ports": "fw_udp_out_ports", "udp_out_ports": "fw_udp_out_ports",
"forward_udp_out_ports": "fw_forward_udp_out_ports", "forward_udp_out_ports": "fw_forward_udp_out_ports",
"blocked_ips": "fw_blocked_ips",
"block_tcp_ports": "fw_block_tcp_ports", "block_tcp_ports": "fw_block_tcp_ports",
"block_udp_ports": "fw_block_udp_ports", "block_udp_ports": "fw_block_udp_ports",
"create_traffic_counter": "fw_create_traffic_counter", "create_traffic_counter": "fw_create_traffic_counter",
@@ -307,6 +328,7 @@ MAIN_V4_ONLY = {
"kernel_log_martians": "fw_kernel_log_martians", "kernel_log_martians": "fw_kernel_log_martians",
"kernel_deactivate_source_route": "fw_kernel_deactivate_source_route", "kernel_deactivate_source_route": "fw_kernel_deactivate_source_route",
"kernel_dont_accept_redirects": "fw_kernel_dont_accept_redirects", "kernel_dont_accept_redirects": "fw_kernel_dont_accept_redirects",
"blocked_ips": "fw_blocked_ips_v4",
} }
# IPv6-only variables (from main_ipv6.conf) # IPv6-only variables (from main_ipv6.conf)
@@ -325,6 +347,16 @@ MAIN_V6_ONLY = {
"xmmp_remote_out_services": "fw_xmmp_remote_out_services_v6", "xmmp_remote_out_services": "fw_xmmp_remote_out_services_v6",
"resolver_allowed_networks": "fw_resolver_allowed_networks_v6", "resolver_allowed_networks": "fw_resolver_allowed_networks_v6",
"kernel_forward_between_interfaces": "fw_kernel_forward_between_interfaces", "kernel_forward_between_interfaces": "fw_kernel_forward_between_interfaces",
"throttle_tcp6_mss_for_https": "fw_throttle_tcp6_mss_for_https",
"tcp6_mss_for_https_val": "fw_tcp6_mss_for_https_val",
"ban_ipv6_mask": "fw_ban_ipv6_mask",
"blocked_ips": "fw_blocked_ips_v6",
}
# Renamed bash variables: old name → new name (applied to $var references)
RENAMED_BASH_VARS = {
"stansard_mattermost_udp_ports_in": "standard_mattermost_udp_ports_in",
"stansard_mattermost_udp_ports_out": "standard_mattermost_udp_ports_out",
} }
@@ -430,6 +462,14 @@ def ssh_cat(host, user, port, path, sudo_password=None):
return None return None
def normalize_value(value):
"""Normalize bash variable references: ${var} → $var, renamed vars → new name."""
value = re.sub(r'\$\{([A-Za-z_][A-Za-z0-9_]*)\}', r'$\1', value)
for old, new in RENAMED_BASH_VARS.items():
value = re.sub(r'\$' + old + r'\b', '$' + new, value)
return value
def coerce_bool(value): def coerce_bool(value):
"""Convert bash true/false string to Python bool, or return string.""" """Convert bash true/false string to Python bool, or return string."""
if value.lower() in ("true", "yes", "1"): if value.lower() in ("true", "yes", "1"):
@@ -456,9 +496,11 @@ def yaml_value(v):
def build_host_vars(parsed_iface_v4, parsed_iface_v6, parsed_main_v4, parsed_main_v6): def build_host_vars(parsed_iface_v4, parsed_iface_v6, parsed_main_v4, parsed_main_v6):
""" """
Map parsed bash variables to Ansible fw_* variables. Map parsed bash variables to Ansible fw_* variables.
Returns dict of {ansible_var: value} containing only non-default values. Returns (dict of {ansible_var: value} containing only non-default values,
list of warnings).
""" """
result = {} result = {}
warnings = []
# --- Interfaces: extract lists from numbered vars --- # --- Interfaces: extract lists from numbered vars ---
def extract_list(parsed, prefix, suffix="", count=3): def extract_list(parsed, prefix, suffix="", count=3):
@@ -501,11 +543,17 @@ def build_host_vars(parsed_iface_v4, parsed_iface_v6, parsed_main_v4, parsed_mai
if v and v != DEFAULTS.get(ansible_var, ""): if v and v != DEFAULTS.get(ansible_var, ""):
result[ansible_var] = v result[ansible_var] = v
# --- Shared main variables (read from ipv4) --- # --- Shared main variables (read from ipv4, fall back to ipv6) ---
for bash_var, ansible_var in MAIN_SHARED.items(): for bash_var, ansible_var in MAIN_SHARED.items():
raw = parsed_main_v4.get(bash_var) raw_v4 = parsed_main_v4.get(bash_var)
raw_v6 = parsed_main_v6.get(bash_var)
if raw_v4 is not None and raw_v6 is not None \
and normalize_value(raw_v4) != normalize_value(raw_v6):
warnings.append(f" # {ansible_var}: IPv4 '{raw_v4}' != IPv6 '{raw_v6}' — IPv4 value used")
raw = raw_v4 if raw_v4 is not None else raw_v6
if raw is None: if raw is None:
continue continue
raw = normalize_value(raw)
v = coerce_bool(raw) if raw.lower() in ("true", "false") else raw v = coerce_bool(raw) if raw.lower() in ("true", "false") else raw
default = DEFAULTS.get(ansible_var) default = DEFAULTS.get(ansible_var)
if v != default: if v != default:
@@ -516,6 +564,7 @@ def build_host_vars(parsed_iface_v4, parsed_iface_v6, parsed_main_v4, parsed_mai
raw = parsed_main_v4.get(bash_var) raw = parsed_main_v4.get(bash_var)
if raw is None: if raw is None:
continue continue
raw = normalize_value(raw)
v = coerce_bool(raw) if raw.lower() in ("true", "false") else raw v = coerce_bool(raw) if raw.lower() in ("true", "false") else raw
default = DEFAULTS.get(ansible_var) default = DEFAULTS.get(ansible_var)
if v != default: if v != default:
@@ -526,12 +575,13 @@ def build_host_vars(parsed_iface_v4, parsed_iface_v6, parsed_main_v4, parsed_mai
raw = parsed_main_v6.get(bash_var) raw = parsed_main_v6.get(bash_var)
if raw is None: if raw is None:
continue continue
raw = normalize_value(raw)
v = coerce_bool(raw) if raw.lower() in ("true", "false") else raw v = coerce_bool(raw) if raw.lower() in ("true", "false") else raw
default = DEFAULTS.get(ansible_var) default = DEFAULTS.get(ansible_var)
if v != default: if v != default:
result[ansible_var] = v result[ansible_var] = v
return result return result, warnings
def render_yaml(hostname, host_vars, all_warnings): def render_yaml(hostname, host_vars, all_warnings):
@@ -540,7 +590,7 @@ def render_yaml(hostname, host_vars, all_warnings):
"---", "---",
f"# ipt-firewall configuration for {hostname}", f"# ipt-firewall configuration for {hostname}",
"# Generated by extract-fw-host-vars.py - review before committing!", "# Generated by extract-fw-host-vars.py - review before committing!",
"# Place in: host_vars/<hostname>/ipt_firewall.yml", "# Place in: host_vars/<hostname>/<short-hostname>-ipt-server.yml",
"", "",
] ]
@@ -574,7 +624,11 @@ def render_yaml(hostname, host_vars, all_warnings):
"fw_allow_local_service_v6", "fw_allow_local_service_from_networks_v6"]), "fw_allow_local_service_v6", "fw_allow_local_service_from_networks_v6"]),
("SSH", ["fw_ssh_server_ips", "fw_forward_ssh_server_ips", "fw_ssh_ports"]), ("SSH", ["fw_ssh_server_ips", "fw_forward_ssh_server_ips", "fw_ssh_ports"]),
("HTTP", ["fw_http_server_ips", "fw_forward_http_server_ips", "fw_http_ports", ("HTTP", ["fw_http_server_ips", "fw_forward_http_server_ips", "fw_http_ports",
"fw_log_cgi_traffic_out", "fw_cgi_script_users"]), "fw_log_cgi_traffic_out", "fw_cgi_script_users",
"fw_set_max_new_http_conn_per_ip_per_second", "fw_max_http_new_conn_per_ip_per_second",
"fw_set_max_concurrent_http_conn_per_ip", "fw_max_concurrent_http_conn_per_ip",
"fw_set_max_new_http_conn_per_second", "fw_max_new_http_conn_per_second",
"fw_throttle_tcp6_mss_for_https", "fw_tcp6_mss_for_https_val"]),
("Mail", ["fw_smtpd_ips", "fw_forward_smtpd_ips", "fw_smtpd_additional_listen_ports", ("Mail", ["fw_smtpd_ips", "fw_forward_smtpd_ips", "fw_smtpd_additional_listen_ports",
"fw_smtpd_additional_outgoing_ports", "fw_mail_server_ips", "fw_forward_mail_server_ips", "fw_smtpd_additional_outgoing_ports", "fw_mail_server_ips", "fw_forward_mail_server_ips",
"fw_mail_user_ports", "fw_mail_client_ips", "fw_forward_mail_client_ips", "fw_mail_user_ports", "fw_mail_client_ips", "fw_forward_mail_client_ips",
@@ -601,7 +655,8 @@ def render_yaml(hostname, host_vars, all_warnings):
"fw_jibri_server_ips", "fw_forward_jibri_server_ips", "fw_jibri_remote_jitsi_server"]), "fw_jibri_server_ips", "fw_forward_jibri_server_ips", "fw_jibri_remote_jitsi_server"]),
("TURN / STUN", ["fw_nc_turn_server_ips", "fw_forward_nc_turn_server_ips", ("TURN / STUN", ["fw_nc_turn_server_ips", "fw_forward_nc_turn_server_ips",
"fw_nc_turn_ports", "fw_nc_turn_udp_ports"]), "fw_nc_turn_ports", "fw_nc_turn_udp_ports"]),
("Mattermost", ["fw_mm_server_ips", "fw_forward_mm_server_ips"]), ("Mattermost", ["fw_mm_server_ips", "fw_forward_mm_server_ips",
"fw_mm_udp_ports_in", "fw_mm_tcp_ports_in", "fw_mm_udp_ports_out"]),
("Prometheus", ["fw_prometheus_local_server_ips", "fw_prometheus_local_client_ips", ("Prometheus", ["fw_prometheus_local_server_ips", "fw_prometheus_local_client_ips",
"fw_prometheus_remote_server_ips"]), "fw_prometheus_remote_server_ips"]),
("Xymon", ["fw_xymon_server_ips", "fw_local_xymon_client", "fw_xymon_port"]), ("Xymon", ["fw_xymon_server_ips", "fw_local_xymon_client", "fw_xymon_port"]),
@@ -610,7 +665,7 @@ def render_yaml(hostname, host_vars, all_warnings):
"fw_udp_out_ports", "fw_forward_udp_out_ports"]), "fw_udp_out_ports", "fw_forward_udp_out_ports"]),
("Portforwarding", ["fw_portforward_tcp_v4", "fw_portforward_udp_v4", ("Portforwarding", ["fw_portforward_tcp_v4", "fw_portforward_udp_v4",
"fw_portforward_tcp_v6", "fw_portforward_udp_v6"]), "fw_portforward_tcp_v6", "fw_portforward_udp_v6"]),
("Block", ["fw_blocked_ips", "fw_block_tcp_ports", "fw_block_udp_ports"]), ("Block", ["fw_blocked_ips_v4", "fw_blocked_ips_v6", "fw_ban_ipv6_mask", "fw_block_tcp_ports", "fw_block_udp_ports"]),
("Protection / limits", ["fw_protection_against_syn_flooding", ("Protection / limits", ["fw_protection_against_syn_flooding",
"fw_protection_against_port_scanning", "fw_protection_against_port_scanning",
"fw_protection_against_ssh_brute_force_attacks", "fw_protection_against_ssh_brute_force_attacks",
@@ -712,10 +767,11 @@ def main():
if warnings: if warnings:
all_warnings.extend([f" # [{key}] {w}" for w in warnings]) all_warnings.extend([f" # [{key}] {w}" for w in warnings])
host_vars = build_host_vars( host_vars, map_warnings = build_host_vars(
parsed["iface_v4"], parsed["iface_v6"], parsed["iface_v4"], parsed["iface_v6"],
parsed["main_v4"], parsed["main_v6"], parsed["main_v4"], parsed["main_v6"],
) )
all_warnings.extend(map_warnings)
yaml_text = render_yaml(hostname, host_vars, all_warnings) yaml_text = render_yaml(hostname, host_vars, all_warnings)
+1 -1
View File
@@ -24,4 +24,4 @@ fw_dns_server_ips: $ext_ips
fw_wireguard_server_ips: $ext_ips fw_wireguard_server_ips: $ext_ips
# --- Block # --- Block
fw_blocked_ips: 222.184.0.0/13 61.160.0.0/16 116.8.0.0/14 fw_blocked_ips_v4: 222.184.0.0/13 61.160.0.0/16 116.8.0.0/14
+1 -1
View File
@@ -24,4 +24,4 @@ fw_dns_server_ips: $ext_ips
fw_wireguard_server_ips: $ext_ips fw_wireguard_server_ips: $ext_ips
# --- Block # --- Block
fw_blocked_ips: 222.184.0.0/13 61.160.0.0/16 116.8.0.0/14 fw_blocked_ips_v4: 222.184.0.0/13 61.160.0.0/16 116.8.0.0/14
@@ -36,4 +36,4 @@ fw_http_server_ips: $ext_ips
fw_tcp_out_ports: 51372 51374 fw_tcp_out_ports: 51372 51374
# --- Block # --- Block
fw_blocked_ips: 222.184.0.0/13 61.160.0.0/16 116.8.0.0/14 fw_blocked_ips_v4: 222.184.0.0/13 61.160.0.0/16 116.8.0.0/14
@@ -21,4 +21,4 @@ munin_remote_ipv6: "2a01:4f9:3070:2bda::227"
fw_http_server_ips: $ext_1_ip fw_http_server_ips: $ext_1_ip
# --- Block # --- Block
fw_blocked_ips: 222.184.0.0/13 61.160.0.0/16 116.8.0.0/14 fw_blocked_ips_v4: 222.184.0.0/13 61.160.0.0/16 116.8.0.0/14
@@ -24,4 +24,4 @@ fw_http_server_ips: $ext_1_ip
fw_mail_client_ips: $ext_1_ip fw_mail_client_ips: $ext_1_ip
# --- Block # --- Block
fw_blocked_ips: 222.184.0.0/13 61.160.0.0/16 116.8.0.0/14 fw_blocked_ips_v4: 222.184.0.0/13 61.160.0.0/16 116.8.0.0/14
@@ -24,4 +24,4 @@ fw_http_server_ips: $ext_1_ip
fw_mail_client_ips: $ext_1_ip fw_mail_client_ips: $ext_1_ip
# --- Block # --- Block
fw_blocked_ips: 222.184.0.0/13 61.160.0.0/16 116.8.0.0/14 fw_blocked_ips_v4: 222.184.0.0/13 61.160.0.0/16 116.8.0.0/14
@@ -24,4 +24,4 @@ fw_http_server_ips: $ext_1_ip
fw_mail_client_ips: $ext_1_ip fw_mail_client_ips: $ext_1_ip
# --- Block # --- Block
fw_blocked_ips: 222.184.0.0/13 61.160.0.0/16 116.8.0.0/14 fw_blocked_ips_v4: 222.184.0.0/13 61.160.0.0/16 116.8.0.0/14
@@ -24,4 +24,4 @@ fw_http_server_ips: $ext_1_ip
fw_mail_client_ips: $ext_1_ip fw_mail_client_ips: $ext_1_ip
# --- Block # --- Block
# fw_blocked_ips: 222.184.0.0/13 61.160.0.0/16 116.8.0.0/14 # fw_blocked_ips_v4: 222.184.0.0/13 61.160.0.0/16 116.8.0.0/14
@@ -36,4 +36,4 @@ fw_mumble_server_ips: 138.201.33.54
fw_rsync_out_ips: $ext_1_ip fw_rsync_out_ips: $ext_1_ip
# --- Block # --- Block
fw_blocked_ips: 222.184.0.0/13 61.160.0.0/16 116.8.0.0/14 fw_blocked_ips_v4: 222.184.0.0/13 61.160.0.0/16 116.8.0.0/14
+6 -6
View File
@@ -17,7 +17,7 @@ Das alte Verfahren:
Das neue Verfahren: Das neue Verfahren:
- Alle Firewall-Einstellungen liegen in `host_vars/${HOSTNAME}/ipt-server.yml`. - Alle Firewall-Einstellungen liegen in `host_vars/${HOSTNAME}/${HOSTNAME%.*.*}-ipt-server.yml`.
- Ansible deployt die Config-Dateien aus Jinja2-Templates. - Ansible deployt die Config-Dateien aus Jinja2-Templates.
- Direktes Editieren auf dem Host ist nicht mehr vorgesehen. - Direktes Editieren auf dem Host ist nicht mehr vorgesehen.
@@ -57,7 +57,7 @@ cd /path/to/ansible/oopen-server
HOSTNAME=<hostname> HOSTNAME=<hostname>
./extract-fw-host-vars.py ${HOSTNAME} --sudo \ ./extract-fw-host-vars.py ${HOSTNAME} --sudo \
-o host_vars/${HOSTNAME}/ipt-server.yml -o host_vars/${HOSTNAME}/${HOSTNAME%.*.*}-ipt-server.yml
``` ```
Das Skript fragt einmalig nach dem `sudo`-Passwort. Das Skript fragt einmalig nach dem `sudo`-Passwort.
@@ -65,7 +65,7 @@ Das Skript fragt einmalig nach dem `sudo`-Passwort.
**Ergebnis prüfen:** **Ergebnis prüfen:**
```bash ```bash
cat host_vars/${HOSTNAME}/ipt-server.yml cat host_vars/${HOSTNAME}/${HOSTNAME%.*.*}-ipt-server.yml
``` ```
Kontrollpunkte: Kontrollpunkte:
@@ -114,7 +114,7 @@ Jetzt wird `fw_manage_config: true` gesetzt, damit Ansible die vier
host-spezifischen Config-Dateien aus den Templates schreibt: host-spezifischen Config-Dateien aus den Templates schreibt:
```yaml ```yaml
# host_vars/${HOSTNAME}/ipt-server.yml # host_vars/${HOSTNAME}/${HOSTNAME%.*.*}-ipt-server.yml
--- ---
fw_manage_config: true # ← hinzufügen / auf true setzen fw_manage_config: true # ← hinzufügen / auf true setzen
@@ -159,7 +159,7 @@ Config-Dateien produzieren exakt dieselben Rules wie die bisher händisch
verwalteten. verwalteten.
Falls Unterschiede erscheinen: die abweichenden Rules identifizieren, die Falls Unterschiede erscheinen: die abweichenden Rules identifizieren, die
entsprechenden Variablen in `host_vars/${HOSTNAME}/ipt-server.yml` nachpflegen, entsprechenden Variablen in `host_vars/${HOSTNAME}/${HOSTNAME%.*.*}-ipt-server.yml` nachpflegen,
erneut ausrollen und den Diff wiederholen. erneut ausrollen und den Diff wiederholen.
Ab jetzt: Ab jetzt:
@@ -204,7 +204,7 @@ Hinweis (via `{{ ansible_managed }}`):
```ini ```ini
# Ansible managed # Ansible managed
# DO NOT EDIT - changes will be overwritten on the next Ansible run. # DO NOT EDIT - changes will be overwritten on the next Ansible run.
# Edit host_vars/${HOSTNAME}/ipt-server.yml instead. # Edit host_vars/${HOSTNAME}/${HOSTNAME%.*.*}-ipt-server.yml instead.
``` ```
--- ---
+29 -5
View File
@@ -42,9 +42,14 @@ roles/ipt-server/
Host-spezifische Konfiguration liegt ausschließlich in: Host-spezifische Konfiguration liegt ausschließlich in:
``` ```
host_vars/${HOSTNAME}/ipt-server.yml host_vars/${HOSTNAME}/${HOSTNAME%.*.*}-ipt-server.yml
``` ```
Der Dateiname beginnt mit dem Hostnamen ohne Domain, z.B.
`host_vars/a.mx.oopen.de/a.mx-ipt-server.yml` oder
`host_vars/backup.oopen.de/backup-ipt-server.yml`
(`${HOSTNAME%.*.*}` schneidet die letzten zwei Labels ab).
--- ---
## Neuen Host aufnehmen ## Neuen Host aufnehmen
@@ -61,7 +66,7 @@ cd /path/to/ansible/oopen-server
HOSTNAME=<hostname> HOSTNAME=<hostname>
mkdir -p host_vars/${HOSTNAME} mkdir -p host_vars/${HOSTNAME}
cat > host_vars/${HOSTNAME}/ipt-server.yml << 'EOF' cat > host_vars/${HOSTNAME}/${HOSTNAME%.*.*}-ipt-server.yml << 'EOF'
--- ---
fw_manage_config: true fw_manage_config: true
@@ -111,7 +116,7 @@ Was passiert:
Alle Änderungen erfolgen ausschließlich in der host_vars-Datei des Hosts: Alle Änderungen erfolgen ausschließlich in der host_vars-Datei des Hosts:
``` ```
host_vars/${HOSTNAME}/ipt-server.yml host_vars/${HOSTNAME}/${HOSTNAME%.*.*}-ipt-server.yml
``` ```
Danach: Danach:
@@ -132,7 +137,7 @@ tatsächlichen Änderungen wird die Firewall neu gestartet.
### Beispiel: HTTP-Server aktivieren ### Beispiel: HTTP-Server aktivieren
```yaml ```yaml
# host_vars/${HOSTNAME}/ipt-server.yml # host_vars/${HOSTNAME}/${HOSTNAME%.*.*}-ipt-server.yml
fw_http_server_ips: "$ext_ips" # oder konkrete IP fw_http_server_ips: "$ext_ips" # oder konkrete IP
``` ```
@@ -172,8 +177,17 @@ chmod 750 $DST/ipt-firewall-server $DST/ip6t-firewall-server
Ebenso für geteilte Conf-Dateien in `roles/ipt-server/files/etc/ipt-firewall/`. Ebenso für geteilte Conf-Dateien in `roles/ipt-server/files/etc/ipt-firewall/`.
Kommen dabei neue Variablen in `main_ipv4.conf.sample` / `main_ipv6.conf.sample`
hinzu, müssen sie zusätzlich nachgezogen werden in:
- `defaults/main.yml` (als `fw_*`-Variable mit Default),
- `templates/etc/ipt-firewall/main_ipv{4,6}.conf.j2`,
- `extract-fw-host-vars.py` (`DEFAULTS`, `MAIN_SHARED` bzw. `MAIN_V4_ONLY`/`MAIN_V6_ONLY`,
Sektion in `render_yaml`).
Nach dem Commit werden die neuen Skripte beim nächsten Ansible-Run auf alle Nach dem Commit werden die neuen Skripte beim nächsten Ansible-Run auf alle
Hosts deployed. Hosts deployed. Geänderte Skripte oder geteilte Conf-Dateien lösen einen
Neustart der Firewall aus.
--- ---
@@ -188,6 +202,16 @@ Hosts deployed.
| `fw_ssh_server_ips` | `"$ext_ips"` | IPs auf denen SSH erlaubt ist | | `fw_ssh_server_ips` | `"$ext_ips"` | IPs auf denen SSH erlaubt ist |
| `fw_ssh_ports` | `"$standard_ssh_port"` | SSH-Port(s) | | `fw_ssh_ports` | `"$standard_ssh_port"` | SSH-Port(s) |
| `fw_http_server_ips` | `""` | IPs auf denen HTTP/HTTPS erlaubt ist | | `fw_http_server_ips` | `""` | IPs auf denen HTTP/HTTPS erlaubt ist |
| `fw_set_max_new_http_conn_per_ip_per_second` | `true` | Neue HTTP(S)-Verbindungen/s pro Quell-IP begrenzen |
| `fw_max_http_new_conn_per_ip_per_second` | `"$default_max_http_new_conn_per_ip_per_second"` | Limit dazu |
| `fw_set_max_concurrent_http_conn_per_ip` | `true` | Gleichzeitige HTTP(S)-Verbindungen pro Quell-IP begrenzen |
| `fw_max_concurrent_http_conn_per_ip` | `"$default_max_concurrent_http_conn_per_ip"` | Limit dazu |
| `fw_set_max_new_http_conn_per_second` | `true` | SYN-Rate auf 80/443 insgesamt begrenzen |
| `fw_max_new_http_conn_per_second` | `"$default_max_new_http_conn_per_second"` | Limit dazu |
| `fw_throttle_tcp6_mss_for_https` | `false` | Nur IPv6: MSS eingehender HTTPS-SYNs clampen |
| `fw_tcp6_mss_for_https_val` | `"$min_tcp6_mss_for_https_val"` | Nur IPv6: MSS-Wert (z.B. `1360`) |
| `fw_mm_tcp_ports_in` | `"$standard_mattermost_tcp_ports_in"` | Mattermost TCP-Fallback (STUN) |
| `fw_ban_ipv6_mask` | `"$default_ban_ipv6_mask"` | Nur IPv6: Präfixlänge für `ban_ipv6.list`-Einträge ohne Maske |
| `munin_remote_ipv4` | `""` | Munin-Server IPv4 | | `munin_remote_ipv4` | `""` | Munin-Server IPv4 |
| `munin_remote_ipv6` | `""` | Munin-Server IPv6 | | `munin_remote_ipv6` | `""` | Munin-Server IPv6 |
+20 -4
View File
@@ -2,7 +2,7 @@
# --- # ---
# ipt-firewall role defaults # ipt-firewall role defaults
# Override per host in host_vars/<hostname>/ipt_firewall.yml # Override per host in host_vars/<hostname>/<short-hostname>-ipt-server.yml
# --- # ---
@@ -156,6 +156,19 @@ fw_http_ports: "$standard_http_ports"
fw_log_cgi_traffic_out: false fw_log_cgi_traffic_out: false
fw_cgi_script_users: "" fw_cgi_script_users: ""
# Restrictions on HTTP(S) connections (ports 80,443) to counteract attacks.
# Values default to the 'default_*' settings in default_settings.conf.
fw_set_max_new_http_conn_per_ip_per_second: true
fw_max_http_new_conn_per_ip_per_second: "$default_max_http_new_conn_per_ip_per_second"
fw_set_max_concurrent_http_conn_per_ip: true
fw_max_concurrent_http_conn_per_ip: "$default_max_concurrent_http_conn_per_ip"
fw_set_max_new_http_conn_per_second: true
fw_max_new_http_conn_per_second: "$default_max_new_http_conn_per_second"
# IPv6 only: clamp the advertised MSS of incoming HTTPS SYN packets.
fw_throttle_tcp6_mss_for_https: false
fw_tcp6_mss_for_https_val: "$min_tcp6_mss_for_https_val"
# --- # ---
# Services: Mattermost # Services: Mattermost
@@ -163,8 +176,9 @@ fw_cgi_script_users: ""
fw_mm_server_ips: "" fw_mm_server_ips: ""
fw_forward_mm_server_ips: "" fw_forward_mm_server_ips: ""
fw_mm_udp_ports_in: "$stansard_mattermost_udp_ports_in" fw_mm_udp_ports_in: "$standard_mattermost_udp_ports_in"
fw_mm_udp_ports_out: "$stansard_mattermost_udp_ports_out" fw_mm_tcp_ports_in: "$standard_mattermost_tcp_ports_in"
fw_mm_udp_ports_out: "$standard_mattermost_udp_ports_out"
# --- # ---
@@ -320,7 +334,9 @@ fw_portforward_udp_v6: ""
# Blocked IPs / ports # Blocked IPs / ports
# --- # ---
fw_blocked_ips: "" fw_blocked_ips_v4: ""
fw_blocked_ips_v6: ""
fw_ban_ipv6_mask: "$default_ban_ipv6_mask" # IPv6 only: prefix length for ban_ipv6.list entries without mask
fw_block_tcp_ports: "111 113 135 137:139 445" fw_block_tcp_ports: "111 113 135 137:139 445"
fw_block_udp_ports: "111 137:139" fw_block_udp_ports: "111 137:139"
@@ -10,8 +10,10 @@
# - # -
# - # -
# - Note: # - Note:
# - - If no mask is given mask will be set to '64' # - - If no mask is given mask will be set to 'ban_ipv6_mask' (main_ipv6.conf),
# - default is '128'
# - - wrong addresses like '2g01::1' or '2a01::1/129' will be ignored # - - wrong addresses like '2g01::1' or '2a01::1/129' will be ignored
# - - entries covering an own (global) address of this host will be ignored
# - # -
# - Example: # - Example:
# - 240e:ec:4ab1:feba:e8b4:4fb1:7984:4c # - 240e:ec:4ab1:feba:e8b4:4fb1:7984:4c
@@ -4,7 +4,7 @@
# --- Default Parameter / Options # --- Default Parameter / Options
# ------------- # -------------
default_per_IP_connection_limit=111 default_per_IP_connection_limit=250
# ------------- # -------------
@@ -56,8 +56,9 @@ standard_prometheus_ports="9100,9256"
# - Mattermost (MM) Service # - Mattermost (MM) Service
# - # -
stansard_mattermost_udp_ports_in="8443" standard_mattermost_udp_ports_in="8443"
stansard_mattermost_udp_ports_out="3478" standard_mattermost_tcp_ports_in="8443"
standard_mattermost_udp_ports_out="3478"
# - IPsec - Internet Security Association and # - IPsec - Internet Security Association and
# - Key Management Protocol # - Key Management Protocol
@@ -70,6 +71,37 @@ standard_ipsec_nat_t=4500
standard_http_ports="80,443" standard_http_ports="80,443"
standard_mailuser_ports="587,465,110,995,143,993" standard_mailuser_ports="587,465,110,995,143,993"
# ip6t-firewall-server:
# Clamp the advertised MSS of incoming HTTPS SYN packets to 1360.
#
throttle_tcp6_mss_for_https=false
min_tcp6_mss_for_https_val=1280
# ip6t-firewall-server,ipt-firewall-server:
# some restrictions on HTTP connections to counteract attacks.
#
# The default value used to be set to '30'
#
set_max_new_http_conn_per_ip_per_second=true
default_max_http_new_conn_per_ip_per_second=200
# The default value used to be set to '50'
#
set_max_concurrent_http_conn_per_ip=true
default_max_concurrent_http_conn_per_ip=100
# The default value used to be set to '200'
#
set_max_new_http_conn_per_second=true
default_max_new_http_conn_per_second=400
# ip6t-firewall-server:
# Prefix length for entries in 'ban_ipv6.list' given without mask.
#
default_ban_ipv6_mask=128
# - Dovecot Service # - Dovecot Service
# - # -
dovecot_external_auth_port="44444" dovecot_external_auth_port="44444"
@@ -85,20 +117,20 @@ default_jitsi_dovecout_auth_port="$dovecot_external_auth_port"
default_jibri_out_port=5222 default_jibri_out_port=5222
# default_outbound_streaming_tcp_ports # default_outbound_streaming_tcp_ports
# #
# - outbound port 1935/TCP : outbound streaming over RTMP to most # - outbound port 1935/TCP : outbound streaming over RTMP to most
# streaming providers such as YouTube Live, Vimeo or Twitch # streaming providers such as YouTube Live, Vimeo or Twitch
# #
# - outbound port 1936/TCP : outbound streaming over RTMP to LinkedIn # - outbound port 1936/TCP : outbound streaming over RTMP to LinkedIn
# Live (port 1935 is also used for RTMP streaming to LinkedIn) # Live (port 1935 is also used for RTMP streaming to LinkedIn)
# #
# - outbound ports 2935/TCP and 2396/TCP : outbound streaming over # - outbound ports 2935/TCP and 2396/TCP : outbound streaming over
# RTMPS to LinkedIn Live # RTMPS to LinkedIn Live
# #
# - outbound port 443/TCP (HTTPS) : used for authentication with the # - outbound port 443/TCP (HTTPS) : used for authentication with the
# built-in providers such as YouTube Live, Facebook Live, Ustream, # built-in providers such as YouTube Live, Facebook Live, Ustream,
# Livestream, and Twitch # Livestream, and Twitch
# #
# - outbound port 53/UDP (DNS) used for DNS lookups converting # - outbound port 53/UDP (DNS) used for DNS lookups converting
# hostnames to IP addresses # hostnames to IP addresses
# #
default_outbound_streaming_tcp_ports="1935,1936,2935,2396" default_outbound_streaming_tcp_ports="1935,1936,2935,2396"
@@ -73,6 +73,54 @@ containsElement () {
return 1 return 1
} }
## - Expand a given IPv6 address (parameter 1) to 32 hex digits without colons.
## - Returns 1 if the address cannot be parsed.
## -
ipv6_expand() {
local _addr="${1,,}"
local _head _tail _group _n _out=""
local -a _h=() _t=()
if [[ "$_addr" == *::* ]] ; then
_head="${_addr%%::*}"
_tail="${_addr#*::}"
[[ "$_tail" == *::* ]] && return 1
[[ -n "$_head" ]] && IFS=':' read -ra _h <<< "$_head"
[[ -n "$_tail" ]] && IFS=':' read -ra _t <<< "$_tail"
_n=$(( 8 - ${#_h[@]} - ${#_t[@]} ))
[[ $_n -lt 1 ]] && return 1
for (( ; _n > 0; _n-- )) ; do
_h+=("0")
done
_h+=("${_t[@]}")
else
IFS=':' read -ra _h <<< "$_addr"
fi
[[ ${#_h[@]} -eq 8 ]] || return 1
for _group in "${_h[@]}" ; do
[[ "$_group" =~ ^[0-9a-f]{1,4}$ ]] || return 1
_out+="$(printf '%04x' "0x$_group")"
done
echo "$_out"
}
## - Check if a given IPv6 address (parameter 1) lies within the network
## - given by address (parameter 2) and prefix length (parameter 3).
## -
ipv6_in_net() {
local _a _n _full _rest
_a="$(ipv6_expand "$1")" || return 1
_n="$(ipv6_expand "$2")" || return 1
_full=$(( $3 / 4 ))
_rest=$(( $3 % 4 ))
[[ "${_a:0:_full}" == "${_n:0:_full}" ]] || return 1
[[ $_rest -eq 0 ]] && return 0
(( (0x${_a:_full:1} >> (4 - _rest)) == (0x${_n:_full:1} >> (4 - _rest)) ))
}
is_number() { is_number() {
return $(test ! -z "${1##*[!0-9]*}" > /dev/null 2>&1); return $(test ! -z "${1##*[!0-9]*}" > /dev/null 2>&1);
@@ -60,7 +60,7 @@ done
# --- # ---
declare -a ext_ip_arr declare -a ext_ip_arr
for _ip in $ext_ips ; do for _ip in $ext_ips ; do
host_ip_arr+=("$_ip") ext_ip_arr+=("$_ip")
done done
# --- # ---
@@ -448,6 +448,29 @@ fi
# ---
# - ICMPv6 Neighbor Discovery and Packet Too Big
# ---
# Accept these before any rule blocking by source address (blocked_ips,
# ban_ipv6.list). Otherwise a blocked network containing a router of the
# own link breaks neighbor discovery - the router can no longer reach this host.
#
# 2 = packet-too-big
# 133= router-solicitation
# 134= router-advertisement
# 135= neighbor-solicitation
# 136= neighbor-advertisement
# ---
echononl "\tICMPv6 neighbor discovery / packet-too-big (before blocking rules).."
for t in 2 133 134 135 136 ; do
$ip6t -A INPUT -p ipv6-icmp --icmpv6-type $t -j ACCEPT
done
if $kernel_forward_between_interfaces ; then
$ip6t -A FORWARD -p ipv6-icmp --icmpv6-type 2 -j ACCEPT
fi
echo_done
# ------------- # -------------
# --- Block IPs / Networks / Interfaces # --- Block IPs / Networks / Interfaces
# ------------- # -------------
@@ -509,6 +532,19 @@ if [[ -f "$conf_ban_ipv6_list" ]] ; then
declare -a ban_ipv6_arr=() declare -a ban_ipv6_arr=()
declare -a no_valid_ipv6=() declare -a no_valid_ipv6=()
declare -a own_ipv6_banned_arr=()
if [[ -z "$ban_ipv6_mask" || ! "$ban_ipv6_mask" =~ ^[0-9]+$ ]] || [[ $ban_ipv6_mask -gt 128 ]] ; then
ban_ipv6_mask=${default_ban_ipv6_mask:-128}
fi
# Own global IPv6 addresses (read at runtime, also covers SLAAC addresses).
# Entries covering one of them would block the own prefix including its routers.
#
declare -a own_ipv6_arr=()
for _own_ip in $(ip -6 -o addr show scope global | awk '{split($4, a, "/"); print a[1]}') ; do
own_ipv6_arr+=("$_own_ip")
done
# Regex valid ipv6 address # Regex valid ipv6 address
# #
@@ -568,7 +604,7 @@ if [[ -f "$conf_ban_ipv6_list" ]] ; then
fi fi
fi fi
else else
mask=64 mask=$ban_ipv6_mask
is_valid_mask=true is_valid_mask=true
fi fi
@@ -586,6 +622,22 @@ if [[ -f "$conf_ban_ipv6_list" ]] ; then
continue continue
fi fi
# Never ban a network containing an own address
#
_covers_own_ip=false
for _own_ip in "${own_ipv6_arr[@]}" ; do
if ipv6_in_net "$_own_ip" "$ipv6" "$mask" ; then
_covers_own_ip=true
break
fi
done
if $_covers_own_ip ; then
if ! containsElement "$_ip" "${own_ipv6_banned_arr[@]}" ; then
own_ipv6_banned_arr+=("$_ip")
fi
continue
fi
for _dev in ${ext_if_arr[@]} ; do for _dev in ${ext_if_arr[@]} ; do
if $log_blocked_ip || $log_all ; then if $log_blocked_ip || $log_all ; then
$ip6t -A INPUT -i $_dev -s $_ip -j $LOG_TARGET $tag_log_prefix "$log_prefix Blocked by ban_ipv6.list: " $ip6t -A INPUT -i $_dev -s $_ip -j $LOG_TARGET $tag_log_prefix "$log_prefix Blocked by ban_ipv6.list: "
@@ -614,6 +666,9 @@ if [[ -f "$conf_ban_ipv6_list" ]] ; then
if [[ ${#no_valid_ipv6_arr[@]} -gt 0 ]]; then if [[ ${#no_valid_ipv6_arr[@]} -gt 0 ]]; then
warn "Ignored: ${no_valid_ipv6_arr[@]}" warn "Ignored: ${no_valid_ipv6_arr[@]}"
fi fi
if [[ ${#own_ipv6_banned_arr[@]} -gt 0 ]]; then
warn "Ignored (covers own address): ${own_ipv6_banned_arr[@]}"
fi
else else
echo_skipped echo_skipped
fi fi
@@ -644,6 +699,59 @@ echo_skipped
#echo_done #echo_done
# ---
# - IPv6 TCP MSS adjustment for incoming HTTPS connections
# ---
# The IPv6 path MTU can be smaller than the interface MTU.
# Clamp the advertised MSS of incoming HTTPS SYN packets to 1360.
# This prevents oversized TCP segments in server responses.
#
# Tested on oolm-shop on 2026-10-26.
# ---
echononl "\tAdjust IPv6 TCP MSS for HTTPS connections"
if $throttle_tcp6_mss_for_https ; then
if [[ -n "$tcp6_mss_for_https_val" ]] ; then
# Notice:
#
# - take care 'tcp6_mss_for_https_val' consists only of digits (is a natural number)
#
# - 1280 is the minimum IPv6 packet size that all routers must be able to process
#
if [[ "$tcp6_mss_for_https_val" =~ ^[0-9]+$ ]] ; then
if [[ "$tcp6_mss_for_https_val" -lt 1280 ]]; then
tcp6_mss_for_https_val=$min_tcp6_mss_for_https_val
fi
fi
else
tcp6_mss_for_https_val=$min_tcp6_mss_for_https_val
fi
for _dev in ${ext_if_arr[@]} ; do
$ip6t -t mangle -A PREROUTING -i $_dev \
-p tcp --dport 443 \
--tcp-flags SYN,RST SYN \
-j TCPMSS --set-mss $tcp6_mss_for_https_val
done
echo_done
else
echo_skipped
fi
# --- # ---
# Drop TCP packets that are new and are not SYN # Drop TCP packets that are new and are not SYN
# --- # ---
@@ -709,13 +817,18 @@ echo_done
# --- # ---
echononl "\tBlock spoofed (own ip) packets" echononl "\tBlock spoofed (own ip) packets"
# Note:
# Exclude loopback - local traffic to an own address (e.g. a web application
# requesting its own domain) passes PREROUTING via 'lo' with src = dst.
#
if $log_spoofed || $log_all ; then if $log_spoofed || $log_all ; then
for _ip in ${ext_ip_arr[@]} ; do for _ip in ${ext_ip_arr[@]} ; do
$ip6t -t mangle -A PREROUTING -s $_ip -d $_ip -j $LOG_TARGET $tag_log_prefix "$log_prefix Spoofed (own ip): " $ip6t -t mangle -A PREROUTING ! -i lo -s $_ip -d $_ip -j $LOG_TARGET $tag_log_prefix "$log_prefix Spoofed (own ip): "
done done
fi fi
for _ip in ${ext_ip_arr[@]} ; do for _ip in ${ext_ip_arr[@]} ; do
$ip6t -t mangle -A PREROUTING -s $_ip -d $_ip -j DROP $ip6t -t mangle -A PREROUTING ! -i lo -s $_ip -d $_ip -j DROP
done done
echo_done echo_done
@@ -2057,6 +2170,134 @@ echo_done
# - HTTP(S) (local) Webserver # - HTTP(S) (local) Webserver
# --- # ---
echo ""
# Note:
# The following rules only DROP/REJECT excess connections - they never ACCEPT.
# Ports are opened exclusively by the rules for 'http_server_ips' below.
# Without local webserver (empty 'http_server_ips') no rules are created.
#
echononl "\t\tA maximum number new connections per second per IP"
if [[ ${#http_server_ip_arr[@]} -gt 0 ]] && $set_max_new_http_conn_per_ip_per_second ; then
if [[ -z "$max_http_new_conn_per_ip_per_second" || ! "$max_http_new_conn_per_ip_per_second" =~ ^[0-9]+$ ]]; then
max_http_new_conn_per_ip_per_second=$default_max_http_new_conn_per_ip_per_second
fi
burst_http_new_conn_per_ip_per_second=$((max_http_new_conn_per_ip_per_second * 2))
if $log_rejected || $log_all ; then
$ip6t -A INPUT -p tcp -m multiport --dports $http_ports -m conntrack --ctstate NEW \
-m hashlimit --hashlimit-name http_v6_log --hashlimit-mode srcip \
--hashlimit-above ${max_http_new_conn_per_ip_per_second}/second \
--hashlimit-burst $burst_http_new_conn_per_ip_per_second \
--hashlimit-htable-expire 600000 \
-m limit --limit 10/second --limit-burst 20 \
-j $LOG_TARGET $tag_log_prefix "$log_prefix more then $max_http_new_conn_per_ip_per_second new connections per second"
fi
# HTTP/HTTPS: neue Verbindungen pro Quell-IP begrenzen
# Über 'max_http_new_conn_per_ip_per_second' neue Verbindungen/s pro IP werden gedroppt
# Eintrag wird nach 600000 ms = 10 Minuten gelöscht (--hashlimit-htable-expire 600000).
$ip6t -A INPUT -p tcp -m multiport --dports $http_ports -m conntrack --ctstate NEW \
-m hashlimit --hashlimit-name http_v6 --hashlimit-mode srcip \
--hashlimit-above ${max_http_new_conn_per_ip_per_second}/second \
--hashlimit-burst $burst_http_new_conn_per_ip_per_second \
--hashlimit-htable-expire 600000 -j DROP
echo_done
else
echo_skipped
fi
echononl "\t\tA maximum number of concurrent TCP connections per IP"
if [[ ${#http_server_ip_arr[@]} -gt 0 ]] && $set_max_concurrent_http_conn_per_ip ; then
if [[ -z "$max_concurrent_http_conn_per_ip" || ! "$max_concurrent_http_conn_per_ip" =~ ^[0-9]+$ ]]; then
max_concurrent_http_conn_per_ip=$default_max_concurrent_http_conn_per_ip
fi
if $log_rejected || $log_all ; then
$ip6t -A INPUT -p tcp -m multiport --dports $http_ports --syn \
-m connlimit --connlimit-above $max_concurrent_http_conn_per_ip --connlimit-mask 128 \
-m limit --limit 10/second --limit-burst 20 \
-j $LOG_TARGET $tag_log_prefix "$log_prefix more then $max_concurrent_http_conn_per_ip concurrent connections per IP."
fi
# HTTP/HTTPS: gleichzeitige Verbindungen pro IP begrenzen
# Mehr als 'max_concurrent_http_conn_per_ip' parallele TCP-Verbindungen pro IP ablehnen
#
# Limit gilt pro einzelner IPv6-Adresse (--connlimit-mask 128) (z.bsp. 2a01:599:108:40b0:e1a9:170b:ba18:fd58/128)
$ip6t -A INPUT -p tcp -m multiport --dports $http_ports --syn \
-m connlimit --connlimit-above $max_concurrent_http_conn_per_ip --connlimit-mask 128 \
-j REJECT --reject-with tcp-reset
echo_done
else
echo_skipped
fi
echononl "\t\tA maximum number of new TCP handshakes per second."
if [[ ${#http_server_ip_arr[@]} -gt 0 ]] && $set_max_new_http_conn_per_second ; then
if [[ -z "$max_new_http_conn_per_second" || ! "$max_new_http_conn_per_second" =~ ^[0-9]+$ ]]; then
max_new_http_conn_per_second=$default_max_new_http_conn_per_second
fi
burst_max_new_http_conn_per_second=$((2 * max_new_http_conn_per_second))
# SYN-Rate begrenzen (global, alle Quell-IPs zusammen)
#
# hashlimit ohne '--hashlimit-mode' verwendet einen einzigen Bucket für alle
# Pakete. SYN-Pakete oberhalb des Limits werden gedroppt; alles darunter
# läuft weiter zu den Regeln für 'http_server_ips' (kein ACCEPT hier!).
#
# Kurzfristig auch bis zu 'burst_max_new_http_conn_per_second' SYN-Pakete erlaubt.
if $log_rejected || $log_all ; then
$ip6t -A INPUT -p tcp -m multiport --dports $http_ports \
--syn \
-m hashlimit --hashlimit-name http_v6_syn_log \
--hashlimit-above ${max_new_http_conn_per_second}/second \
--hashlimit-burst $burst_max_new_http_conn_per_second \
-m limit --limit 10/second --limit-burst 20 \
-j $LOG_TARGET $tag_log_prefix "$log_prefix SYN-Overflow."
fi
$ip6t -A INPUT -p tcp -m multiport --dports $http_ports \
--syn \
-m hashlimit --hashlimit-name http_v6_syn \
--hashlimit-above ${max_new_http_conn_per_second}/second \
--hashlimit-burst $burst_max_new_http_conn_per_second \
-j DROP
echo_done
else
echo_skipped
fi
echo ""
echononl "\t\tHTTP(S) (local) Webserver" echononl "\t\tHTTP(S) (local) Webserver"
if [[ ${#http_server_ip_arr[@]} -gt 0 ]] || [[ ${#forward_http_server_ip_arr[@]} -gt 0 ]] ; then if [[ ${#http_server_ip_arr[@]} -gt 0 ]] || [[ ${#forward_http_server_ip_arr[@]} -gt 0 ]] ; then
@@ -2089,12 +2330,22 @@ if [[ ${#mm_server_ip_arr[@]} -gt 0 ]] || [[ ${#forward_mm_server_ip_arr[@]} -gt
if [[ ${#mm_server_ip_arr[@]} -gt 0 ]] ; then if [[ ${#mm_server_ip_arr[@]} -gt 0 ]] ; then
for _ip in ${mm_server_ip_arr[@]} ; do for _ip in ${mm_server_ip_arr[@]} ; do
$ip6t -A INPUT -p udp -d $_ip -m multiport --dports $mm_udp_ports_in -m conntrack --ctstate NEW -j ACCEPT $ip6t -A INPUT -p udp -d $_ip -m multiport --dports $mm_udp_ports_in -m conntrack --ctstate NEW -j ACCEPT
if [[ -n "$mm_tcp_ports_in" ]] ; then
$ip6t -A INPUT -p tcp -d $_ip -m multiport --dports $mm_tcp_ports_in -m conntrack --ctstate NEW -j ACCEPT
else
$ip6t -A INPUT -p tcp -d $_ip -m multiport --dports $mm_udp_ports_in -m conntrack --ctstate NEW -j ACCEPT
fi
$ip6t -A OUTPUT -p udp -s $_ip -m multiport --dports $mm_udp_ports_out -m conntrack --ctstate NEW -j ACCEPT $ip6t -A OUTPUT -p udp -s $_ip -m multiport --dports $mm_udp_ports_out -m conntrack --ctstate NEW -j ACCEPT
done done
if [[ ${#forward_mm_server_ip_arr[@]} -gt 0 ]] && $kernel_forward_between_interfaces ; then if [[ ${#forward_mm_server_ip_arr[@]} -gt 0 ]] && $kernel_forward_between_interfaces ; then
for _ip in ${forward_mm_server_ip_arr[@]} ; do for _ip in ${forward_mm_server_ip_arr[@]} ; do
$ip6t -A FORWARD -p udp -d $_ip -m multiport --dports $mm_udp_ports_in -m conntrack --ctstate NEW -j ACCEPT $ip6t -A FORWARD -p udp -d $_ip -m multiport --dports $mm_udp_ports_in -m conntrack --ctstate NEW -j ACCEPT
if [[ -n "$mm_tcp_ports_in" ]] ; then
$ip6t -A FORWARD -p tcp -d $_ip -m multiport --dports $mm_tcp_ports_in -m conntrack --ctstate NEW -j ACCEPT
else
$ip6t -A FORWARD -p tcp -d $_ip -m multiport --dports $mm_udp_ports_in -m conntrack --ctstate NEW -j ACCEPT
fi
$ip6t -A FORWARD -p udp -s $_ip -m multiport --dports $mm_udp_ports_out -m conntrack --ctstate NEW -j ACCEPT $ip6t -A FORWARD -p udp -s $_ip -m multiport --dports $mm_udp_ports_out -m conntrack --ctstate NEW -j ACCEPT
done done
fi fi
@@ -814,13 +814,18 @@ echo_done
# --- # ---
echononl "\tBlock spoofed (own ip) packets" echononl "\tBlock spoofed (own ip) packets"
# Note:
# Exclude loopback - local traffic to an own address (e.g. a web application
# requesting its own domain) passes PREROUTING via 'lo' with src = dst.
#
if $log_spoofed || $log_all ; then if $log_spoofed || $log_all ; then
for _ip in ${ext_ip_arr[@]} ; do for _ip in ${ext_ip_arr[@]} ; do
$ipt -t mangle -A PREROUTING -s $_ip -d $_ip -j $LOG_TARGET $tag_log_prefix "$log_prefix Spoofed (own ip): " $ipt -t mangle -A PREROUTING ! -i lo -s $_ip -d $_ip -j $LOG_TARGET $tag_log_prefix "$log_prefix Spoofed (own ip): "
done done
fi fi
for _ip in ${ext_ip_arr[@]} ; do for _ip in ${ext_ip_arr[@]} ; do
$ipt -t mangle -A PREROUTING -s $_ip -d $_ip -j DROP $ipt -t mangle -A PREROUTING ! -i lo -s $_ip -d $_ip -j DROP
done done
echo_done echo_done
@@ -2241,6 +2246,134 @@ echo_done
# - HTTP(S) (local) Webserver # - HTTP(S) (local) Webserver
# --- # ---
echo ""
# Note:
# The following rules only DROP/REJECT excess connections - they never ACCEPT.
# Ports are opened exclusively by the rules for 'http_server_ips' below.
# Without local webserver (empty 'http_server_ips') no rules are created.
#
echononl "\t\tA maximum number new connections per second per IP"
if [[ ${#http_server_ip_arr[@]} -gt 0 ]] && $set_max_new_http_conn_per_ip_per_second ; then
if [[ -z "$max_http_new_conn_per_ip_per_second" || ! "$max_http_new_conn_per_ip_per_second" =~ ^[0-9]+$ ]]; then
max_http_new_conn_per_ip_per_second=$default_max_http_new_conn_per_ip_per_second
fi
burst_http_new_conn_per_ip_per_second=$((max_http_new_conn_per_ip_per_second * 2))
if $log_rejected || $log_all ; then
$ipt -A INPUT -p tcp -m multiport --dports $http_ports -m conntrack --ctstate NEW \
-m hashlimit --hashlimit-name http_v4_log --hashlimit-mode srcip \
--hashlimit-above ${max_http_new_conn_per_ip_per_second}/second \
--hashlimit-burst $burst_http_new_conn_per_ip_per_second \
--hashlimit-htable-expire 600000 \
-m limit --limit 10/second --limit-burst 20 \
-j $LOG_TARGET $tag_log_prefix "$log_prefix more then $max_http_new_conn_per_ip_per_second new connections per second"
fi
# HTTP/HTTPS: neue Verbindungen pro Quell-IP begrenzen
# Über 'max_http_new_conn_per_ip_per_second' neue Verbindungen/s pro IP werden gedroppt
# Eintrag wird nach 600000 ms = 10 Minuten gelöscht (--hashlimit-htable-expire 600000).
$ipt -A INPUT -p tcp -m multiport --dports $http_ports -m conntrack --ctstate NEW \
-m hashlimit --hashlimit-name http_v4 --hashlimit-mode srcip \
--hashlimit-above ${max_http_new_conn_per_ip_per_second}/second \
--hashlimit-burst $burst_http_new_conn_per_ip_per_second \
--hashlimit-htable-expire 600000 -j DROP
echo_done
else
echo_skipped
fi
echononl "\t\tA maximum number of concurrent TCP connections per IP"
if [[ ${#http_server_ip_arr[@]} -gt 0 ]] && $set_max_concurrent_http_conn_per_ip ; then
if [[ -z "$max_concurrent_http_conn_per_ip" || ! "$max_concurrent_http_conn_per_ip" =~ ^[0-9]+$ ]]; then
max_concurrent_http_conn_per_ip=$default_max_concurrent_http_conn_per_ip
fi
if $log_rejected || $log_all ; then
$ipt -A INPUT -p tcp -m multiport --dports $http_ports --syn \
-m connlimit --connlimit-above $max_concurrent_http_conn_per_ip --connlimit-mask 32 \
-m limit --limit 10/second --limit-burst 20 \
-j $LOG_TARGET $tag_log_prefix "$log_prefix more then $max_concurrent_http_conn_per_ip concurrent connections per IP."
fi
# HTTP/HTTPS: gleichzeitige Verbindungen pro IP begrenzen
# Mehr als 'max_concurrent_http_conn_per_ip' parallele TCP-Verbindungen pro IP ablehnen
#
# Limit gilt pro einzelner IPv4-Adresse (--connlimit-mask 32)
$ipt -A INPUT -p tcp -m multiport --dports $http_ports --syn \
-m connlimit --connlimit-above $max_concurrent_http_conn_per_ip --connlimit-mask 32 \
-j REJECT --reject-with tcp-reset
echo_done
else
echo_skipped
fi
echononl "\t\tA maximum number of new TCP handshakes per second."
if [[ ${#http_server_ip_arr[@]} -gt 0 ]] && $set_max_new_http_conn_per_second ; then
if [[ -z "$max_new_http_conn_per_second" || ! "$max_new_http_conn_per_second" =~ ^[0-9]+$ ]]; then
max_new_http_conn_per_second=$default_max_new_http_conn_per_second
fi
burst_max_new_http_conn_per_second=$((2 * max_new_http_conn_per_second))
# SYN-Rate begrenzen (global, alle Quell-IPs zusammen)
#
# hashlimit ohne '--hashlimit-mode' verwendet einen einzigen Bucket für alle
# Pakete. SYN-Pakete oberhalb des Limits werden gedroppt; alles darunter
# läuft weiter zu den Regeln für 'http_server_ips' (kein ACCEPT hier!).
#
# Kurzfristig auch bis zu 'burst_max_new_http_conn_per_second' SYN-Pakete erlaubt.
if $log_rejected || $log_all ; then
$ipt -A INPUT -p tcp -m multiport --dports $http_ports \
--syn \
-m hashlimit --hashlimit-name http_v4_syn_log \
--hashlimit-above ${max_new_http_conn_per_second}/second \
--hashlimit-burst $burst_max_new_http_conn_per_second \
-m limit --limit 10/second --limit-burst 20 \
-j $LOG_TARGET $tag_log_prefix "$log_prefix SYN-Overflow."
fi
$ipt -A INPUT -p tcp -m multiport --dports $http_ports \
--syn \
-m hashlimit --hashlimit-name http_v4_syn \
--hashlimit-above ${max_new_http_conn_per_second}/second \
--hashlimit-burst $burst_max_new_http_conn_per_second \
-j DROP
echo_done
else
echo_skipped
fi
echo ""
echononl "\t\tHTTP(S) (local) Webserver" echononl "\t\tHTTP(S) (local) Webserver"
if [[ ${#http_server_ip_arr[@]} -gt 0 ]] || [[ ${#forward_http_server_ip_arr[@]} -gt 0 ]] ; then if [[ ${#http_server_ip_arr[@]} -gt 0 ]] || [[ ${#forward_http_server_ip_arr[@]} -gt 0 ]] ; then
@@ -2273,12 +2406,22 @@ if [[ ${#mm_server_ip_arr[@]} -gt 0 ]] || [[ ${#forward_mm_server_ip_arr[@]} -gt
if [[ ${#mm_server_ip_arr[@]} -gt 0 ]] ; then if [[ ${#mm_server_ip_arr[@]} -gt 0 ]] ; then
for _ip in ${mm_server_ip_arr[@]} ; do for _ip in ${mm_server_ip_arr[@]} ; do
$ipt -A INPUT -p udp -d $_ip -m multiport --dports $mm_udp_ports_in -m conntrack --ctstate NEW -j ACCEPT $ipt -A INPUT -p udp -d $_ip -m multiport --dports $mm_udp_ports_in -m conntrack --ctstate NEW -j ACCEPT
$ipt -A OUTPUT -p udp -s $_ip -m multiport --dports $mm_udp_ports_out -m conntrack --ctstate NEW -j ACCEPT if [[ -n "$mm_tcp_ports_in" ]] ; then
$ipt -A INPUT -p tcp -d $_ip -m multiport --dports $mm_tcp_ports_in -m conntrack --ctstate NEW -j ACCEPT
else
$ipt -A INPUT -p tcp -d $_ip -m multiport --dports $mm_udp_ports_in -m conntrack --ctstate NEW -j ACCEPT
fi
$ipt -A OUTPUT -p udp -s $_ip -m multiport --dports $mm_udp_ports_out -m conntrack --ctstate NEW -j ACCEPT
done done
if [[ ${#forward_mm_server_ip_arr[@]} -gt 0 ]] && $kernel_activate_forwarding ; then if [[ ${#forward_mm_server_ip_arr[@]} -gt 0 ]] && $kernel_activate_forwarding ; then
for _ip in ${forward_mm_server_ip_arr[@]} ; do for _ip in ${forward_mm_server_ip_arr[@]} ; do
$ipt -A FORWARD -p udp -d $_ip -m multiport --dports $mm_udp_ports_in -m conntrack --ctstate NEW -j ACCEPT $ipt -A FORWARD -p udp -d $_ip -m multiport --dports $mm_udp_ports_in -m conntrack --ctstate NEW -j ACCEPT
if [[ -n "$mm_tcp_ports_in" ]] ; then
$ipt -A FORWARD -p tcp -d $_ip -m multiport --dports $mm_tcp_ports_in -m conntrack --ctstate NEW -j ACCEPT
else
$ipt -A FORWARD -p tcp -d $_ip -m multiport --dports $mm_udp_ports_in -m conntrack --ctstate NEW -j ACCEPT
fi
$ipt -A FORWARD -p udp -s $_ip -m multiport --dports $mm_udp_ports_out -m conntrack --ctstate NEW -j ACCEPT $ipt -A FORWARD -p udp -s $_ip -m multiport --dports $mm_udp_ports_out -m conntrack --ctstate NEW -j ACCEPT
done done
fi fi
+9
View File
@@ -107,6 +107,8 @@
owner: root owner: root
group: root group: root
mode: "0750" mode: "0750"
notify:
- Restart IPv4 Firewall
- name: Deploy ip6t-firewall-server - name: Deploy ip6t-firewall-server
copy: copy:
@@ -115,6 +117,8 @@
owner: root owner: root
group: root group: root
mode: "0750" mode: "0750"
notify:
- Restart IPv6 Firewall
# === # ===
@@ -134,6 +138,11 @@
- logging_ipv4.conf - logging_ipv4.conf
- logging_ipv6.conf - logging_ipv6.conf
- post_declarations.conf - post_declarations.conf
# Shared files are sourced by both scripts; logging_ipv4/6 only by one,
# but restarting both keeps this simple.
notify:
- Restart IPv4 Firewall
- Restart IPv6 Firewall
# === # ===
@@ -137,6 +137,16 @@ http_ports="{{ fw_http_ports }}"
log_cgi_traffic_out={{ fw_log_cgi_traffic_out | lower }} log_cgi_traffic_out={{ fw_log_cgi_traffic_out | lower }}
cgi_script_users="{{ fw_cgi_script_users }}" cgi_script_users="{{ fw_cgi_script_users }}"
# Restrictions on HTTP(S) connections to counteract attacks
set_max_new_http_conn_per_ip_per_second={{ fw_set_max_new_http_conn_per_ip_per_second | lower }}
max_http_new_conn_per_ip_per_second="{{ fw_max_http_new_conn_per_ip_per_second }}"
set_max_concurrent_http_conn_per_ip={{ fw_set_max_concurrent_http_conn_per_ip | lower }}
max_concurrent_http_conn_per_ip="{{ fw_max_concurrent_http_conn_per_ip }}"
set_max_new_http_conn_per_second={{ fw_set_max_new_http_conn_per_second | lower }}
max_new_http_conn_per_second="{{ fw_max_new_http_conn_per_second }}"
# ------------- # -------------
# --- Services: Mattermost # --- Services: Mattermost
@@ -145,6 +155,7 @@ cgi_script_users="{{ fw_cgi_script_users }}"
mm_server_ips="{{ fw_mm_server_ips }}" mm_server_ips="{{ fw_mm_server_ips }}"
forward_mm_server_ips="{{ fw_forward_mm_server_ips }}" forward_mm_server_ips="{{ fw_forward_mm_server_ips }}"
mm_udp_ports_in="{{ fw_mm_udp_ports_in }}" mm_udp_ports_in="{{ fw_mm_udp_ports_in }}"
mm_tcp_ports_in="{{ fw_mm_tcp_ports_in }}"
mm_udp_ports_out="{{ fw_mm_udp_ports_out }}" mm_udp_ports_out="{{ fw_mm_udp_ports_out }}"
@@ -305,7 +316,7 @@ portforward_udp="{{ fw_portforward_udp_v4 }}"
# --- Blocked IPs / Ports # --- Blocked IPs / Ports
# ------------- # -------------
blocked_ips="{{ fw_blocked_ips }}" blocked_ips="{{ fw_blocked_ips_v4 }}"
block_tcp_ports="{{ fw_block_tcp_ports }}" block_tcp_ports="{{ fw_block_tcp_ports }}"
block_udp_ports="{{ fw_block_udp_ports }}" block_udp_ports="{{ fw_block_udp_ports }}"
@@ -127,6 +127,20 @@ http_ports="{{ fw_http_ports }}"
log_cgi_traffic_out={{ fw_log_cgi_traffic_out | lower }} log_cgi_traffic_out={{ fw_log_cgi_traffic_out | lower }}
cgi_script_users="{{ fw_cgi_script_users }}" cgi_script_users="{{ fw_cgi_script_users }}"
# Restrictions on HTTP(S) connections to counteract attacks
set_max_new_http_conn_per_ip_per_second={{ fw_set_max_new_http_conn_per_ip_per_second | lower }}
max_http_new_conn_per_ip_per_second="{{ fw_max_http_new_conn_per_ip_per_second }}"
set_max_concurrent_http_conn_per_ip={{ fw_set_max_concurrent_http_conn_per_ip | lower }}
max_concurrent_http_conn_per_ip="{{ fw_max_concurrent_http_conn_per_ip }}"
set_max_new_http_conn_per_second={{ fw_set_max_new_http_conn_per_second | lower }}
max_new_http_conn_per_second="{{ fw_max_new_http_conn_per_second }}"
# Clamp the advertised MSS of incoming HTTPS SYN packets
throttle_tcp6_mss_for_https={{ fw_throttle_tcp6_mss_for_https | lower }}
tcp6_mss_for_https_val="{{ fw_tcp6_mss_for_https_val }}"
# ------------- # -------------
# --- Services: Mattermost # --- Services: Mattermost
@@ -135,6 +149,7 @@ cgi_script_users="{{ fw_cgi_script_users }}"
mm_server_ips="{{ fw_mm_server_ips }}" mm_server_ips="{{ fw_mm_server_ips }}"
forward_mm_server_ips="{{ fw_forward_mm_server_ips }}" forward_mm_server_ips="{{ fw_forward_mm_server_ips }}"
mm_udp_ports_in="{{ fw_mm_udp_ports_in }}" mm_udp_ports_in="{{ fw_mm_udp_ports_in }}"
mm_tcp_ports_in="{{ fw_mm_tcp_ports_in }}"
mm_udp_ports_out="{{ fw_mm_udp_ports_out }}" mm_udp_ports_out="{{ fw_mm_udp_ports_out }}"
@@ -295,7 +310,11 @@ portforward_udp="{{ fw_portforward_udp_v6 }}"
# --- Blocked IPs / Ports # --- Blocked IPs / Ports
# ------------- # -------------
blocked_ips="{{ fw_blocked_ips }}" blocked_ips="{{ fw_blocked_ips_v6 }}"
# Prefix length for entries in ban_ipv6.list given without mask
ban_ipv6_mask="{{ fw_ban_ipv6_mask }}"
block_tcp_ports="{{ fw_block_tcp_ports }}" block_tcp_ports="{{ fw_block_tcp_ports }}"
block_udp_ports="{{ fw_block_udp_ports }}" block_udp_ports="{{ fw_block_udp_ports }}"