Mini Shell
"""Webshield related iptables rules."""
from typing import AbstractSet, Iterator, Mapping
from im360.internals.core import firewall
from im360.internals.core.firewall import FirewallRules
from im360.subsys import webshield
from im360.subsys.webshield_mode import (
get_module_based_ports,
Mode as WebshieldMode,
)
from defence360agent.utils.validate import IPVersion
from .port import redirect_port_rules
from .types_ import FirewallRule, WebshieldRuleBuilder
def rules(
ipset_name: str, ip_version: IPVersion, gray_rules: WebshieldRuleBuilder
) -> Iterator[FirewallRule]:
"""Yield iptables *ip_version* rules for *ipset_name* using
*gray_rules* builder.
This is intended for ipsets that require webshield to implement
their behavior e.g., to show captcha, splashscreen to the ips from
the ipset
"""
current_mode = WebshieldMode.get()
if WebshieldMode.wants_redirect(current_mode):
redirect_map = webshield.port_redirect_map()
dest_ports = webshield.redirected_to_webshield_ports(
current_mode
) & set(redirect_map)
else:
dest_ports = get_module_based_ports()
redirect_map = {port: port for port in dest_ports}
yield from _redirect_rules(
ipset_name, ip_version, redirect_map, dest_ports, gray_rules
)
if not WebshieldMode.wants_redirect(current_mode):
if firewall.is_nat_available(ip_version):
yield from gray_rules.logdrop_chain_rules(ipset_name)
else:
yield from gray_rules.drop_tproxy_rules(ipset_name)
return
if firewall.is_nat_available(ip_version):
yield from gray_rules.logdrop_chain_rules(ipset_name)
yield from redirect_port_rules(
ipset_name,
dest_ports,
redirect_map,
FirewallRules.NAT,
FirewallRules.redirect_to_captcha,
)
else:
# What we are doing if we encounter with centos 6 and ipv6?
# Full description is available into:
# https://cloudlinux.atlassian.net/browse/DEF-2898
# https://access.redhat.com/solutions/311493
# Mark traffic to http, https hosts and from graylist ip
yield from redirect_port_rules(
ipset_name,
dest_ports,
redirect_map,
FirewallRules.MANGLE,
FirewallRules.redirect_to_captcha_via_tproxy,
)
yield from gray_rules.drop_tproxy_rules(ipset_name)
def _redirect_rules(
ipset_name: str,
ip_version: IPVersion,
redirect_map: Mapping[int, int],
dest_ports: AbstractSet[int],
gray_rules: WebshieldRuleBuilder,
) -> Iterator[FirewallRule]:
"""Yield rules for to-be-redirected ports"""
current_mode = WebshieldMode.get()
if WebshieldMode.wants_redirect(current_mode):
yield from check_access_to_webshield_ports_rules(
ipset_name, set(redirect_map[p] for p in dest_ports)
)
yield from gray_rules.open_webshield_ports_for_localhost_rules(
ip_version
)
yield from gray_rules.block_webshield_ports_rules(
redirect_map, dest_ports
)
yield from gray_rules.redirect_panel_ports(ip_version)
# WARN: Module-based Apache/nginx must not be redirected to Webshield
# and therefore their ports (80 & 443 by default) must have ACCEPT rules
# for i360.ipv4.graylist and i360.ipv4.graysplashlist.
# Otherwise, when IP is into one of these ipsets, Apache/nginx is unable
# to response with SplashJS page - it will be dropped by next rule in chain
# imunify360_log_gl.
if current_mode == WebshieldMode.STANDALONE:
module_based_ports = set()
else:
module_based_ports = get_module_based_ports()
yield FirewallRule(
rule=FirewallRules.open_dst_ports_for_src_list(
ipset_name,
set(redirect_map[p] for p in dest_ports) | module_based_ports,
),
)
def check_access_to_webshield_ports_rules(
ipset_name: str, dest_ports: AbstractSet[int]
) -> Iterator[FirewallRule]:
yield FirewallRule(
chain=FirewallRules.WEBSHIELD_PORTS_INPUT_CHAIN,
rule=FirewallRules.open_dst_ports_for_src_list(
ipset_name, dest_ports, policy=FirewallRules.RETURN
),
)