def customer_ipsec_gateway(remote_endpoint, remote_subnets, local_subnets, snat, allow_incoming_traffic=False): logger.trace( "remote_endpoint=%s, remote_subnets=%s, local_subnets=%s, snat=%s" % (remote_endpoint, remote_subnets, local_subnets, snat) ) result = [] result.append(ipsec(remote_endpoint, ah=False)) r_forward = firewall.IPTablesRuleset("filter", "FORWARD") if snat is not None: real_local_subnets = [] r_snat = firewall.IPTablesRuleset("nat", "POSTROUTING") for source_replace in snat: source = source_replace["source"] replace_with = source_replace["replace_with"] r_snat.add(s=source, d=remote_subnets, r="-j SNAT --to-source %s" % replace_with) real_local_subnets.append(source) result.append(r_snat) r_forward.add(s=real_local_subnets, d=remote_subnets, r="-j ACCEPT") if allow_incoming_traffic is True: r_forward.add(d=real_local_subnets, s=remote_subnets, r="-j ACCEPT") else: r_forward.add(s=local_subnets, d=remote_subnets, r="-j ACCEPT") if allow_incoming_traffic is True: r_forward.add(d=local_subnets, s=remote_subnets, r="-j ACCEPT") result.append(r_forward) return result
def input_nrpe(s): logger.trace("s=%s" % s) r = firewall.IPTablesRuleset("filter", "INPUT") r.add(s=s, r="-p tcp -m tcp --dport 5666 -j ACCEPT") h = firewall.HostsAllowRuleset() h.add(daemon="nrpe", s=s) return [r, h]
def start(chain): logger.trace("chain=%s" % chain) r = firewall.IPTablesRuleset("filter", chain) r.add(r="-m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT") r.add(r="-m conntrack --ctstate INVALID -j DROP") if chain in ("INPUT", "OUTPUT"): r.add(d="ff02::/16", r="-j ACCEPT") return r
def output_http(d, port=None): logger.trace("d=%s, port=%s" % (d, port)) if port is None: port = [80, 443] (match, mport) = _group_multiport("tcp", port) r_out = firewall.IPTablesRuleset("filter", "OUTPUT") r_out.add(d=d, r="-p tcp -m %s --dport %s -j ACCEPT" % (match, mport)) return r_out
def early_lo(): logger.trace("") r_input_lo = firewall.IPTablesRuleset("filter", "INPUT") r_input_lo.add(i="lo", r="-j ACCEPT") r_output_lo = firewall.IPTablesRuleset("filter", "OUTPUT") r_output_lo.add(o="lo", r="-j ACCEPT") h = firewall.HostsAllowRuleset() h.add(daemon="sshd", s="localhost.") return [r_input_lo, r_output_lo, h]
def input_udp(s, port=None, comment=None): logger.trace("s=%s, port=%s, comment=%s" % (s, port, comment)) r = firewall.IPTablesRuleset("filter", "INPUT") rule = [] if port is not None: rule.append("-p udp -m %s --dport %s" % _group_multiport("udp", port)) rule.append("-j ACCEPT") r.add(comment=comment, s=s, r=" ".join(rule)) return r
def input_ssh(s, comment=None): logger.trace("s=%s, comment=%s" % (s, comment)) r = firewall.IPTablesRuleset("filter", "INPUT") r.add(comment=comment, s=s, r="-p tcp -m tcp --dport 22 -j ACCEPT") h = firewall.HostsAllowRuleset() if len(s) == 0: h.add(comment=comment, daemon="sshd", s="all") else: h.add(comment=comment, daemon="sshd", s=s) return [r, h]
def output_udp(d, port=None, comment=None, uid=None): logger.trace("d=%s, port=%s, comment=%s, uid=%s" % (d, port, comment, uid)) r = firewall.IPTablesRuleset("filter", "OUTPUT") rule = [] if port is not None: rule.append("-p udp -m %s --dport %s" % _group_multiport("udp", port)) if uid is not None: rule.append("-m owner --uid-owner %s" % uid) rule.append("-j ACCEPT") r.add(comment=comment, d=d, r=" ".join(rule)) return r
def git_commit(outdir, filenames, dry_run, module_name=None): filenames = flatten(filenames) full_filenames = [os.path.join(outdir, filename) for filename in filenames] git_dir = os.path.join(outdir, '.git') if os.path.isdir(git_dir) is False: logger.info("Skipping git actions for %s, no git dir %s" % (filenames, git_dir)) return if os.stat(git_dir).st_uid != os.geteuid(): logger.info("Skipping git actions for %s, git dir %s not owned by us" % (filenames, git_dir)) return env = os.environ.copy() git_env = { 'GIT_DIR': git_dir, 'GIT_WORK_TREE': outdir, } env.update(git_env) if dry_run is True: cmd_status = ['git', 'status', '--porcelain'] cmd_status.extend(filenames) proc_status = subprocess.Popen(cmd_status, env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout_status, stderr_status = proc_status.communicate() if proc_status.returncode != 0: raise Exception("Unable to do git status in %s: %s" % (outdir, stderr_status)) logger.info("Git: changes in %s: %s" % (outdir, stdout_status.splitlines())) else: cmd_add = ['git', 'add'] cmd_add.extend(full_filenames) logger.trace("Running command %s with GIT env %s" % (cmd_add, git_env)) proc_add = subprocess.Popen(cmd_add, env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE) _, stderr_add = proc_add.communicate() if proc_add.returncode != 0: raise Exception("Adding changes in %s failed: %s" % (outdir, stderr_add)) logger.info("Git: commit %s" % filenames) cmd_commit = ['git', 'commit', '-m'] if module_name is not None: cmd_commit.append('commiting changes after caretakr run: %s' % module_name) else: cmd_commit.append('commiting changes after caretakr run') cmd_commit.extend(full_filenames) logger.trace("Running command %s with GIT env %s" % (cmd_commit, git_env)) proc_commit = subprocess.Popen(cmd_commit, env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE) _, stderr_commit = proc_commit.communicate() if proc_commit.returncode != 0 and stderr_commit != '': raise Exception("Commiting changes in %s failed: %s" % (outdir, stderr_commit))
def ipsec(endpoints, esp=True, ah=True): logger.trace("endpoints=%s" % endpoints) r_in = firewall.IPTablesRuleset("filter", "INPUT") if esp is True: r_in.add(s=endpoints, r="-p esp -j ACCEPT") if ah is True: r_in.add(s=endpoints, r="-p ah -j ACCEPT") r_in.add(s=endpoints, r="-p udp -m udp --dport 500 -j ACCEPT") r_out = firewall.IPTablesRuleset("filter", "OUTPUT") if esp is True: r_out.add(d=endpoints, r="-p esp -j ACCEPT") if ah is True: r_out.add(d=endpoints, r="-p ah -j ACCEPT") r_out.add(d=endpoints, r="-p udp -m udp --dport 500 -j ACCEPT") return [r_in, r_out]
def log(chain, ignore=None, limit=None, burst=None): logger.trace("chain=%s, ignore=%s, limit=%s, burst=%s" % (chain, ignore, limit, burst)) if ignore is None: ignore = [] if limit is None: limit = 10 if burst is None: burst = 100 ignore = map(str, firewall.flatten(ignore)) if "all" in ignore: return [] r = firewall.IPTablesRuleset("filter", chain) if "udp" in ignore: r.add(r="-p udp -j DROP") if "tcphigh" in ignore: r.add(r="-p tcp -m multiport --dport 135:139,445,1024:65535 -j DROP") r.add(r="-m limit --limit %s/sec --limit-burst %s " '-j LOG --log-prefix "IPT %s DEAD: "' % (limit, burst, chain)) return r
def input_http(s, port=None, notrack=False): logger.trace("s=%s, port=%s, notrack=%s" % (s, port, notrack)) if port is None: port = [80, 443] (match, mport) = _group_multiport("tcp", port) r_in = firewall.IPTablesRuleset("filter", "INPUT") r_in.add(s=s, r="-p tcp -m %s --dport %s -j ACCEPT" % (match, mport)) result = [r_in] if notrack is True: r_out = firewall.IPTablesRuleset("filter", "OUTPUT") r_out.add(r="-p tcp -m %s --sport %s -j ACCEPT" % (match, mport)) result.append(r_out) r_rawpre = firewall.IPTablesRuleset("raw", "PREROUTING") r_rawpre.add(r="-p tcp -m %s --dport %s -j CT --notrack" % (match, mport)) result.append(r_rawpre) r_rawout = firewall.IPTablesRuleset("raw", "OUTPUT") r_rawout.add(r="-p tcp -m %s --sport %s -j CT --notrack" % (match, mport)) result.append(r_rawout) return result
def input_dns(s, notrack=False): logger.trace("s=%s, notrack=%s" % (s, notrack)) r_in = firewall.IPTablesRuleset("filter", "INPUT") r_in.add(s=s, r="-p udp -m udp --dport 53 -j ACCEPT") r_in.add(s=s, r="-p tcp -m tcp --dport 53 -j ACCEPT") result = [r_in] if notrack is True: r_out = firewall.IPTablesRuleset("filter", "OUTPUT") r_out.add(r="-p udp -m udp --sport 53 -j ACCEPT") r_out.add(r="-p tcp -m tcp --sport 53 -j ACCEPT") result.append(r_out) r_rawpre = firewall.IPTablesRuleset("raw", "PREROUTING") r_rawpre.add(r="-p udp -m udp --dport 53 -j CT --notrack") r_rawpre.add(r="-p tcp -m tcp --dport 53 -j CT --notrack") result.append(r_rawpre) r_rawout = firewall.IPTablesRuleset("raw", "OUTPUT") r_rawout.add(r="-p udp -m udp --sport 53 -j CT --notrack") r_rawout.add(r="-p tcp -m tcp --sport 53 -j CT --notrack") result.append(r_rawout) return result
def _expand_interfaces(interfaces, depth=0, maxdepth=10): done = True logger.trace("interfaces before: (depth %s)\n%s" % (depth, yaml.dump(interfaces))) interfaces = {k: util.flatten(v) for (k, v) in interfaces.iteritems()} for name in list(sorted(interfaces.keys())): interfaces[name] = util.flatten(interfaces[name]) logger.trace("looking at %s: %s" % (name, interfaces[name])) for i in range(len(interfaces[name])): iface = interfaces[name][i] if isinstance(iface, str) and iface in interfaces and iface != name: interfaces[name][i] = interfaces[iface] logger.trace("replacing %s with %s" % (iface, interfaces[iface])) done = False logger.trace("interfaces after: (depth %s)\n%s" % (depth, yaml.dump(interfaces))) if not done: if depth < maxdepth: interfaces = _expand_interfaces(interfaces, depth+1, maxdepth) else: raise Exception("Maximum recursion depth exceeded for _expand_interfaces") return interfaces
def load(config): logger.trace("config: %s" % config) icmp_stateful = config.pop('icmp_stateful', True) ipv6_nat = config.pop('ipv6_nat', False) interfaces = _expand_interfaces(config.pop('interfaces', {})) input_ssh_from = check_wildcard(config.pop('input_ssh_from', [])) input_munin_from = check_wildcard(config.pop('input_munin_from', None)) input_nrpe_from = check_wildcard(config.pop('input_nrpe_from', None)) input_dns_from = check_wildcard(config.pop('input_dns_from', None)) input_dns_notrack = config.pop('input_dns_notrack', False) input_http_from = check_wildcard(config.pop('input_http_from', None)) input_http_port = check_wildcard(config.pop('input_http_port', [80, 443])) input_http_notrack = config.pop('input_http_notrack', False) input_smtp_from = check_wildcard(config.pop('input_smtp_from', None)) input_postgresql_from = check_wildcard(config.pop('input_postgresql_from', None)) input_xmpp_c2s_from = check_wildcard(config.pop('input_xmpp_c2s_from', None)) input_nsca_from = check_wildcard(config.pop('input_nsca_from', None)) output_dns_to = check_wildcard(config.pop('output_dns_to', [])) output_ntp_to = check_wildcard(config.pop('output_ntp_to', [])) output_http_to = check_wildcard(config.pop('output_http_to', None)) output_http_port = check_wildcard(config.pop('output_http_port', [80, 443])) output_smtp_to = check_wildcard(config.pop('output_smtp_to', [])) output_munin_to = check_wildcard(config.pop('output_munin_to', None)) output_nsca_to = check_wildcard(config.pop('output_nsca_to', None)) output_postgresql_to = check_wildcard(config.pop('output_postgresql_to', None)) output_xmpp_c2s_to = check_wildcard(config.pop('output_xmpp_c2s_to', None)) output_user = config.pop('output_user', None) output_group = config.pop('output_group', None) xmpp_s2s_fromto = check_wildcard(config.pop('xmpp_s2s_fromto', None)) gre_peers = check_wildcard(config.pop('gre_peers', None)) if any([input_dns_notrack, input_http_notrack]): logger.debug("notrack option set, early accepting icmp") icmp_stateful = False input_tcp = config.pop('input_tcp', []) input_udp = config.pop('input_udp', []) output_tcp = config.pop('output_tcp', []) output_udp = config.pop('output_udp', []) custom = _custom_rules(config.pop('custom', {}), interfaces) input_log_ignore = config.pop('input_log_ignore', None) input_log_limit = config.pop('input_log_limit', None) input_log_burst = config.pop('input_log_burst', None) output_log_ignore = config.pop('output_log_ignore', None) output_log_limit = config.pop('output_log_limit', None) output_log_burst = config.pop('output_log_burst', None) forwarding = False stateful_forward = config.pop('stateful_forward', True) customer_ipsec_gateway = util.flatten(config.pop('customer_ipsec_gateway', [])) if len(customer_ipsec_gateway) > 0: logger.debug("customer ipsec gateway set, enabling forwarding") forwarding = True fh = IPTablesHostsAllow() fh.fw.enable_ipv6_nat(ipv6_nat) logger.debug("creating rules for early phase") fh.add(libow.early_lo()) if input_dns_from is not None and input_dns_notrack is True: fh.add(libow.input_dns(input_dns_from, input_dns_notrack)) if input_http_from is not None and input_http_notrack is True: fh.add(libow.input_http(input_http_from, input_http_port, input_http_notrack)) fh.add(custom['early'].values()) logger.debug("creating rules for start phase") if icmp_stateful is False: fh.add(libow.input_icmp()) fh.add(libow.output_icmp()) fh.add(libow.start('INPUT')) fh.add(libow.start('OUTPUT')) if icmp_stateful is True: fh.add(libow.stateful_input_icmp()) fh.add(libow.output_icmp()) if forwarding is True: if stateful_forward is True: fh.add(libow.start('FORWARD')) fh.add(libow.forward_icmp()) fh.add(custom['start'].values()) logger.debug("creating rules for middle phase") if input_ssh_from is not None: fh.add(libow.input_ssh(input_ssh_from)) if input_munin_from is not None: fh.add(libow.input_tcp(input_munin_from, 4949)) if input_nrpe_from is not None: fh.add(libow.input_nrpe(input_nrpe_from)) if input_dns_from is not None and input_dns_notrack is False: fh.add(libow.input_dns(input_dns_from, input_dns_notrack)) if input_http_from is not None and input_http_notrack is False: fh.add(libow.input_http(input_http_from, input_http_port, input_http_notrack)) if input_smtp_from is not None: fh.add(libow.input_tcp(input_smtp_from, 25)) if input_postgresql_from is not None: fh.add(libow.input_tcp(input_postgresql_from, 5432)) if input_xmpp_c2s_from is not None: fh.add(libow.input_tcp(input_xmpp_c2s_from, 5222)) fh.add(libow.input_tcp([], 7777)) # XXX: unfiltered? for rule in input_tcp: comment = rule.get('comment', None) s = check_wildcard(rule.get('from', [])) port = rule.get('port', None) fh.add(libow.input_tcp(comment=comment, s=s, port=port)) for rule in input_udp: comment = rule.get('comment', None) s = check_wildcard(rule.get('from', [])) port = rule.get('port', None) fh.add(libow.input_udp(comment=comment, s=s, port=port)) if input_nsca_from is not None: fh.add(libow.input_tcp(input_nsca_from, 5667)) if xmpp_s2s_fromto is not None: fh.add(libow.input_tcp(xmpp_s2s_fromto, 5269)) if gre_peers is not None: fh.add(libow.input_gre(gre_peers)) if output_dns_to is not None: fh.add(libow.output_dns(output_dns_to)) if output_ntp_to is not None: fh.add(libow.output_udp(output_ntp_to, 123, uid='ntp')) if output_http_to is not None: fh.add(libow.output_http(output_http_to, output_http_port)) if output_smtp_to is not None: fh.add(libow.output_tcp(output_smtp_to, 25, uid='postfix')) if output_munin_to is not None: fh.add(libow.output_tcp(output_munin_to, 4949, uid='munin')) if output_nsca_to is not None: fh.add(libow.output_tcp(output_nsca_to, 5667)) if output_postgresql_to is not None: fh.add(libow.output_tcp(output_postgresql_to, 5432)) if output_xmpp_c2s_to is not None: fh.add(libow.output_tcp(output_xmpp_c2s_to, 5222)) if xmpp_s2s_fromto is not None: fh.add(libow.output_tcp(xmpp_s2s_fromto, 5269, uid='ejabberd')) if gre_peers is not None: fh.add(libow.output_gre(gre_peers)) for rule in output_tcp: comment = rule.get('comment', None) d = check_wildcard(rule.get('to', [])) port = rule.get('port', None) uid = rule.get('uid', None) fh.add(libow.output_tcp(comment=comment, d=d, port=port, uid=uid)) for rule in output_udp: comment = rule.get('comment', None) d = check_wildcard(rule.get('to', [])) port = rule.get('port', None) uid = rule.get('uid', None) fh.add(libow.output_udp(comment=comment, d=d, port=port, uid=uid)) if output_user is not None: output_user = util.flatten(output_user) for user in output_user: if isinstance(user, str): fh.add(libow.output_tcp(d=[], uid=user)) elif isinstance(user, int): fh.add(libow.output_tcp(d=[], uid=str(user))) else: raise Exception("don't understand output_user %s" % user) if output_group is not None: output_group = util.flatten(output_group) for group in output_group: if isinstance(group, str): fh.add(libow.output_tcp(d=[], gid=group)) elif isinstance(group, int): fh.add(libow.output_tcp(d=[], gid=str(group))) else: raise Exception("don't understand output_group %s" % group) if len(customer_ipsec_gateway) > 0: for cig in customer_ipsec_gateway: fh.add(libow.customer_ipsec_gateway( remote_endpoint=cig['remote_endpoint'], remote_subnets=cig['remote_subnets'], local_subnets=cig['local_subnets'], allow_incoming_traffic=cig.get('allow_incoming_traffic', False), snat=cig.get('snat', None) )) fh.add(custom['middle'].values()) logger.debug("creating rules for end phase") fh.add(libow.input_traceroute()) fh.add(libow.output_beheer_root()) fh.add(custom['end'].values()) logger.debug("creating rules for late phase") fh.add(libow.log('INPUT', input_log_ignore, input_log_limit, input_log_burst)) fh.add(libow.log('OUTPUT', output_log_ignore, output_log_limit, output_log_burst)) fh.add(custom['late'].values()) for hosts_allow in config.pop('hosts_allow', []): daemon = hosts_allow.pop('daemon') s = hosts_allow.pop('from', None) if '*' in util.flatten(s): s = 'all' if len(hosts_allow) > 0: logger.warn("Unrecognized options in hosts_allow rule: %s" % hosts_allow) fh.add(libow.hosts_allow(daemon, s)) firewall_lines = fh.fw.get_iptables_restore_script(4).splitlines() firewall6_lines = fh.fw.get_iptables_restore_script(6).splitlines() hosts_allow_lines = fh.ha.get_hosts_allow_content().splitlines() additional_config = None def run(outdir, diff, no_write, no_exec, no_git): if any([ util.write_file(outdir, 'network/firewall', firewall_lines, no_write, diff), util.write_file(outdir, 'network/firewall6', firewall6_lines, no_write, diff), util.write_file(outdir, 'hosts.allow', hosts_allow_lines, no_write, diff), ]): if os.geteuid() == 0: if no_exec is True: util.cmd(('/sbin/iptables-restore', '-t'), dry_run=False, stdin=firewall_lines) util.cmd(('/sbin/ip6tables-restore', '-t'), dry_run=False, stdin=firewall6_lines) else: util.cmd(('/sbin/iptables-restore'), dry_run=False, stdin=firewall_lines) util.cmd(('/sbin/ip6tables-restore'), dry_run=False, stdin=firewall6_lines) util.git_commit(outdir, ['network/firewall', 'network/firewall6', 'hosts.allow'], no_git, 'firewall') return run, additional_config
def output_dns(d): logger.trace("d=%s" % d) r_out = firewall.IPTablesRuleset("filter", "OUTPUT") r_out.add(d=d, r="-p udp -m udp --dport 53 -j ACCEPT") r_out.add(d=d, r="-p tcp -m tcp --dport 53 -j ACCEPT") return r_out
def hosts_allow(daemon, s): logger.trace("daemon=%s, s=%s" % (daemon, s)) h = firewall.HostsAllowRuleset() h.add(daemon=daemon, s=s) return h
def output_beheer_root(): logger.trace("") r = firewall.IPTablesRuleset("filter", "OUTPUT") r.add(r="-m owner --uid-owner 0 -j ACCEPT") r.add(r="-m owner --uid-owner 487 -j ACCEPT") return r
def forward_icmp(): logger.trace("") r = firewall.IPTablesRuleset("filter", "FORWARD") r.add(i=if_4, r="-p icmp -j ACCEPT") r.add(i=if_6, r="-p ipv6-icmp -j ACCEPT") return r
def output_icmp(): logger.trace("") r = firewall.IPTablesRuleset("filter", "OUTPUT") r.add(o=if_4, r="-p icmp -j ACCEPT") r.add(o=if_6, r="-p ipv6-icmp -j ACCEPT") return r
def stateful_input_icmp(): logger.trace("") r = firewall.IPTablesRuleset("filter", "INPUT") r.add(i=if_4, r="-p icmp -m icmp --icmp-type 8 -j ACCEPT") r.add(i=if_6, r="-p ipv6-icmp -j ACCEPT") return r
def input_traceroute(): logger.trace("") r = firewall.IPTablesRuleset("filter", "INPUT") r.add(r="-p udp -m multiport --dport 33434:33535 -j REJECT") return r
def input_gre(s=None): logger.trace("s=%s" % (s)) r = firewall.IPTablesRuleset("filter", "INPUT") r.add(s=s, r="-p gre -j ACCEPT") return r
def output_gre(d=None): logger.trace("d=%s" % (d)) r = firewall.IPTablesRuleset("filter", "OUTPUT") r.add(d=d, r="-p gre -j ACCEPT") return r