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:
+68
-12
@@ -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.
|
||||
# ---------------------------------------------------------------------------
|
||||
DEFAULTS = {
|
||||
@@ -73,8 +73,19 @@ DEFAULTS = {
|
||||
"fw_http_ports": "$standard_http_ports",
|
||||
"fw_log_cgi_traffic_out": False,
|
||||
"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_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_forward_smtpd_ips": "",
|
||||
"fw_smtpd_additional_listen_ports": "",
|
||||
@@ -141,7 +152,9 @@ DEFAULTS = {
|
||||
"fw_portforward_udp_v4": "",
|
||||
"fw_portforward_tcp_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_udp_ports": "111 137:139",
|
||||
"fw_create_traffic_counter": True,
|
||||
@@ -207,8 +220,17 @@ MAIN_SHARED = {
|
||||
"http_ports": "fw_http_ports",
|
||||
"log_cgi_traffic_out": "fw_log_cgi_traffic_out",
|
||||
"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",
|
||||
"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",
|
||||
"forward_smtpd_ips": "fw_forward_smtpd_ips",
|
||||
"smtpd_additional_listen_ports": "fw_smtpd_additional_listen_ports",
|
||||
@@ -265,7 +287,6 @@ MAIN_SHARED = {
|
||||
"forward_tcp_out_ports": "fw_forward_tcp_out_ports",
|
||||
"udp_out_ports": "fw_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_udp_ports": "fw_block_udp_ports",
|
||||
"create_traffic_counter": "fw_create_traffic_counter",
|
||||
@@ -307,6 +328,7 @@ MAIN_V4_ONLY = {
|
||||
"kernel_log_martians": "fw_kernel_log_martians",
|
||||
"kernel_deactivate_source_route": "fw_kernel_deactivate_source_route",
|
||||
"kernel_dont_accept_redirects": "fw_kernel_dont_accept_redirects",
|
||||
"blocked_ips": "fw_blocked_ips_v4",
|
||||
}
|
||||
|
||||
# IPv6-only variables (from main_ipv6.conf)
|
||||
@@ -325,6 +347,16 @@ MAIN_V6_ONLY = {
|
||||
"xmmp_remote_out_services": "fw_xmmp_remote_out_services_v6",
|
||||
"resolver_allowed_networks": "fw_resolver_allowed_networks_v6",
|
||||
"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
|
||||
|
||||
|
||||
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):
|
||||
"""Convert bash true/false string to Python bool, or return string."""
|
||||
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):
|
||||
"""
|
||||
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 = {}
|
||||
warnings = []
|
||||
|
||||
# --- Interfaces: extract lists from numbered vars ---
|
||||
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, ""):
|
||||
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():
|
||||
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:
|
||||
continue
|
||||
raw = normalize_value(raw)
|
||||
v = coerce_bool(raw) if raw.lower() in ("true", "false") else raw
|
||||
default = DEFAULTS.get(ansible_var)
|
||||
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)
|
||||
if raw is None:
|
||||
continue
|
||||
raw = normalize_value(raw)
|
||||
v = coerce_bool(raw) if raw.lower() in ("true", "false") else raw
|
||||
default = DEFAULTS.get(ansible_var)
|
||||
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)
|
||||
if raw is None:
|
||||
continue
|
||||
raw = normalize_value(raw)
|
||||
v = coerce_bool(raw) if raw.lower() in ("true", "false") else raw
|
||||
default = DEFAULTS.get(ansible_var)
|
||||
if v != default:
|
||||
result[ansible_var] = v
|
||||
|
||||
return result
|
||||
return result, 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}",
|
||||
"# 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"]),
|
||||
("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",
|
||||
"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",
|
||||
"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",
|
||||
@@ -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"]),
|
||||
("TURN / STUN", ["fw_nc_turn_server_ips", "fw_forward_nc_turn_server_ips",
|
||||
"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",
|
||||
"fw_prometheus_remote_server_ips"]),
|
||||
("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"]),
|
||||
("Portforwarding", ["fw_portforward_tcp_v4", "fw_portforward_udp_v4",
|
||||
"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",
|
||||
"fw_protection_against_port_scanning",
|
||||
"fw_protection_against_ssh_brute_force_attacks",
|
||||
@@ -712,10 +767,11 @@ def main():
|
||||
if 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["main_v4"], parsed["main_v6"],
|
||||
)
|
||||
all_warnings.extend(map_warnings)
|
||||
|
||||
yaml_text = render_yaml(hostname, host_vars, all_warnings)
|
||||
|
||||
|
||||
@@ -24,4 +24,4 @@ fw_dns_server_ips: $ext_ips
|
||||
fw_wireguard_server_ips: $ext_ips
|
||||
|
||||
# --- 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_dns_server_ips: $ext_ips
|
||||
fw_wireguard_server_ips: $ext_ips
|
||||
|
||||
# --- 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
|
||||
|
||||
# --- 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
|
||||
|
||||
# --- 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
|
||||
|
||||
# --- 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
|
||||
|
||||
# --- 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
|
||||
|
||||
# --- 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
|
||||
|
||||
# --- 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
|
||||
|
||||
# --- 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
|
||||
|
||||
@@ -17,7 +17,7 @@ Das alte 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.
|
||||
- Direktes Editieren auf dem Host ist nicht mehr vorgesehen.
|
||||
|
||||
@@ -57,7 +57,7 @@ cd /path/to/ansible/oopen-server
|
||||
HOSTNAME=<hostname>
|
||||
|
||||
./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.
|
||||
@@ -65,7 +65,7 @@ Das Skript fragt einmalig nach dem `sudo`-Passwort.
|
||||
**Ergebnis prüfen:**
|
||||
|
||||
```bash
|
||||
cat host_vars/${HOSTNAME}/ipt-server.yml
|
||||
cat host_vars/${HOSTNAME}/${HOSTNAME%.*.*}-ipt-server.yml
|
||||
```
|
||||
|
||||
Kontrollpunkte:
|
||||
@@ -114,7 +114,7 @@ Jetzt wird `fw_manage_config: true` gesetzt, damit Ansible die vier
|
||||
host-spezifischen Config-Dateien aus den Templates schreibt:
|
||||
|
||||
```yaml
|
||||
# host_vars/${HOSTNAME}/ipt-server.yml
|
||||
# host_vars/${HOSTNAME}/${HOSTNAME%.*.*}-ipt-server.yml
|
||||
---
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
Ab jetzt:
|
||||
@@ -204,7 +204,7 @@ Hinweis (via `{{ ansible_managed }}`):
|
||||
```ini
|
||||
# Ansible managed
|
||||
# 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.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -42,9 +42,14 @@ roles/ipt-server/
|
||||
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
|
||||
@@ -61,7 +66,7 @@ cd /path/to/ansible/oopen-server
|
||||
HOSTNAME=<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
|
||||
|
||||
@@ -111,7 +116,7 @@ Was passiert:
|
||||
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:
|
||||
@@ -132,7 +137,7 @@ tatsächlichen Änderungen wird die Firewall neu gestartet.
|
||||
### Beispiel: HTTP-Server aktivieren
|
||||
|
||||
```yaml
|
||||
# host_vars/${HOSTNAME}/ipt-server.yml
|
||||
# host_vars/${HOSTNAME}/${HOSTNAME%.*.*}-ipt-server.yml
|
||||
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/`.
|
||||
|
||||
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
|
||||
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_ports` | `"$standard_ssh_port"` | SSH-Port(s) |
|
||||
| `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_ipv6` | `""` | Munin-Server IPv6 |
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
# ---
|
||||
# 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_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
|
||||
@@ -163,8 +176,9 @@ fw_cgi_script_users: ""
|
||||
|
||||
fw_mm_server_ips: ""
|
||||
fw_forward_mm_server_ips: ""
|
||||
fw_mm_udp_ports_in: "$stansard_mattermost_udp_ports_in"
|
||||
fw_mm_udp_ports_out: "$stansard_mattermost_udp_ports_out"
|
||||
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"
|
||||
|
||||
|
||||
# ---
|
||||
@@ -320,7 +334,9 @@ fw_portforward_udp_v6: ""
|
||||
# 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_udp_ports: "111 137:139"
|
||||
|
||||
|
||||
@@ -10,8 +10,10 @@
|
||||
# -
|
||||
# -
|
||||
# - 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
|
||||
# - - entries covering an own (global) address of this host will be ignored
|
||||
# -
|
||||
# - Example:
|
||||
# - 240e:ec:4ab1:feba:e8b4:4fb1:7984:4c
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
# --- 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
|
||||
# -
|
||||
stansard_mattermost_udp_ports_in="8443"
|
||||
stansard_mattermost_udp_ports_out="3478"
|
||||
standard_mattermost_udp_ports_in="8443"
|
||||
standard_mattermost_tcp_ports_in="8443"
|
||||
standard_mattermost_udp_ports_out="3478"
|
||||
|
||||
# - IPsec - Internet Security Association and
|
||||
# - Key Management Protocol
|
||||
@@ -70,6 +71,37 @@ standard_ipsec_nat_t=4500
|
||||
standard_http_ports="80,443"
|
||||
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_external_auth_port="44444"
|
||||
|
||||
@@ -73,6 +73,54 @@ containsElement () {
|
||||
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() {
|
||||
|
||||
return $(test ! -z "${1##*[!0-9]*}" > /dev/null 2>&1);
|
||||
|
||||
@@ -60,7 +60,7 @@ done
|
||||
# ---
|
||||
declare -a ext_ip_arr
|
||||
for _ip in $ext_ips ; do
|
||||
host_ip_arr+=("$_ip")
|
||||
ext_ip_arr+=("$_ip")
|
||||
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
|
||||
# -------------
|
||||
@@ -509,6 +532,19 @@ if [[ -f "$conf_ban_ipv6_list" ]] ; then
|
||||
|
||||
declare -a ban_ipv6_arr=()
|
||||
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
|
||||
#
|
||||
@@ -568,7 +604,7 @@ if [[ -f "$conf_ban_ipv6_list" ]] ; then
|
||||
fi
|
||||
fi
|
||||
else
|
||||
mask=64
|
||||
mask=$ban_ipv6_mask
|
||||
is_valid_mask=true
|
||||
fi
|
||||
|
||||
@@ -586,6 +622,22 @@ if [[ -f "$conf_ban_ipv6_list" ]] ; then
|
||||
continue
|
||||
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
|
||||
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: "
|
||||
@@ -614,6 +666,9 @@ if [[ -f "$conf_ban_ipv6_list" ]] ; then
|
||||
if [[ ${#no_valid_ipv6_arr[@]} -gt 0 ]]; then
|
||||
warn "Ignored: ${no_valid_ipv6_arr[@]}"
|
||||
fi
|
||||
if [[ ${#own_ipv6_banned_arr[@]} -gt 0 ]]; then
|
||||
warn "Ignored (covers own address): ${own_ipv6_banned_arr[@]}"
|
||||
fi
|
||||
else
|
||||
echo_skipped
|
||||
fi
|
||||
@@ -644,6 +699,59 @@ echo_skipped
|
||||
#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
|
||||
# ---
|
||||
@@ -709,13 +817,18 @@ echo_done
|
||||
# ---
|
||||
|
||||
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
|
||||
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
|
||||
fi
|
||||
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
|
||||
echo_done
|
||||
|
||||
@@ -2057,6 +2170,134 @@ echo_done
|
||||
# - 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"
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
done
|
||||
|
||||
if [[ ${#forward_mm_server_ip_arr[@]} -gt 0 ]] && $kernel_forward_between_interfaces ; then
|
||||
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
|
||||
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
|
||||
done
|
||||
fi
|
||||
|
||||
@@ -814,13 +814,18 @@ echo_done
|
||||
# ---
|
||||
|
||||
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
|
||||
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
|
||||
fi
|
||||
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
|
||||
echo_done
|
||||
|
||||
@@ -2241,6 +2246,134 @@ echo_done
|
||||
# - 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"
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
|
||||
if [[ ${#forward_mm_server_ip_arr[@]} -gt 0 ]] && $kernel_activate_forwarding ; then
|
||||
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
|
||||
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
|
||||
done
|
||||
fi
|
||||
|
||||
@@ -107,6 +107,8 @@
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0750"
|
||||
notify:
|
||||
- Restart IPv4 Firewall
|
||||
|
||||
- name: Deploy ip6t-firewall-server
|
||||
copy:
|
||||
@@ -115,6 +117,8 @@
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0750"
|
||||
notify:
|
||||
- Restart IPv6 Firewall
|
||||
|
||||
|
||||
# ===
|
||||
@@ -134,6 +138,11 @@
|
||||
- logging_ipv4.conf
|
||||
- logging_ipv6.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 }}
|
||||
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
|
||||
@@ -145,6 +155,7 @@ cgi_script_users="{{ fw_cgi_script_users }}"
|
||||
mm_server_ips="{{ fw_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 }}"
|
||||
|
||||
|
||||
@@ -305,7 +316,7 @@ portforward_udp="{{ fw_portforward_udp_v4 }}"
|
||||
# --- Blocked IPs / Ports
|
||||
# -------------
|
||||
|
||||
blocked_ips="{{ fw_blocked_ips }}"
|
||||
blocked_ips="{{ fw_blocked_ips_v4 }}"
|
||||
block_tcp_ports="{{ fw_block_tcp_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 }}
|
||||
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
|
||||
@@ -135,6 +149,7 @@ cgi_script_users="{{ fw_cgi_script_users }}"
|
||||
mm_server_ips="{{ fw_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 }}"
|
||||
|
||||
|
||||
@@ -295,7 +310,11 @@ portforward_udp="{{ fw_portforward_udp_v6 }}"
|
||||
# --- 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_udp_ports="{{ fw_block_udp_ports }}"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user