ffho_netfilter.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. #
  2. # FFHO netfilter helper functions
  3. #
  4. import ipaddress
  5. def generate_service_rules (services, acls, af):
  6. rules = []
  7. for srv in services:
  8. rule = ""
  9. comment = srv['descr']
  10. acl_comment = ""
  11. src_prefixes = []
  12. # If there are no DST IPs set at all or DST IPs for this AF set, we have a rule to build,
  13. # if this is NOT the case, there is no rule for this AF to generate, carry on.
  14. if not ((not srv['ips']['4'] and not srv['ips']['6']) or srv['ips'][str(af)]):
  15. continue
  16. # Is/are IP(s) set for this service?
  17. if srv['ips'][str(af)]:
  18. rule += "ip" if af == 4 else "ip6"
  19. dst_ips = srv['ips'][str(af)]
  20. if len (dst_ips) == 1:
  21. rule += " daddr %s " % dst_ips[0]
  22. else:
  23. rule += " daddr { %s } " % ", ".join (dst_ips)
  24. # ACLs defined for this service?
  25. if srv['acl']:
  26. srv_acl = sorted (srv['acl'])
  27. for ace in srv_acl:
  28. ace_pfx = (acls[ace][af])
  29. # Many entries
  30. if type (ace_pfx) == list:
  31. src_prefixes.extend (ace_pfx)
  32. else:
  33. src_prefixes.append (ace_pfx)
  34. acl_comment = "acl: %s" % ", ".join (srv_acl)
  35. # Additional prefixes defined for this service?
  36. if srv['additional_prefixes']:
  37. add_pfx = []
  38. # Additional prefixes are given as a space separated list
  39. for entry in srv['additional_prefixes'].split ():
  40. # Strip commas and spaces, just in case
  41. pfx_str = entry.strip (" ,")
  42. pfx_obj = ipaddress.ip_network (pfx_str)
  43. # We only care for additional pfx for this AF
  44. if pfx_obj.version != af:
  45. continue
  46. add_pfx.append (pfx_str)
  47. if add_pfx:
  48. src_prefixes.extend (add_pfx)
  49. if acl_comment:
  50. acl_comment += ", "
  51. acl_comment += "additional pfx"
  52. # Combine ACL + additional prefixes (if any)
  53. if src_prefixes:
  54. rule += "ip" if af == 4 else "ip6"
  55. if len (src_prefixes) > 1:
  56. rule += " saddr { %s } " % ", ".join (src_prefixes)
  57. else:
  58. rule += " saddr %s " % src_prefixes[0]
  59. if acl_comment:
  60. comment += " (%s)" % acl_comment
  61. # Multiple ports?
  62. if len (srv['ports']) > 1:
  63. ports = "{ %s }" % ", ".join (map (str, sorted (srv['ports'])))
  64. else:
  65. ports = srv['ports'][0]
  66. rule += "%s dport %s counter accept comment \"%s\"" % (srv['proto'], ports, comment)
  67. rules.append (rule)
  68. return rules
  69. def generate_forward_policy (policy, roles, config_context):
  70. fp = {
  71. # Get default policy for packets to be forwarded
  72. 'policy' : 'drop',
  73. 'policy_reason' : 'default',
  74. 'rules': {
  75. 4 : [],
  76. 6 : [],
  77. },
  78. }
  79. if 'forward_default_policy' in policy:
  80. fp['policy'] = policy['forward_default_policy']
  81. fp['policy_reason'] = 'forward_default_policy'
  82. # Does any local role warrants for forwarding packets?
  83. accept_roles = [role for role in policy.get ('forward_accept_roles', []) if role in roles]
  84. if accept_roles:
  85. fp['policy'] = 'accept'
  86. fp['policy_reason'] = "roles: " + ",".join (accept_roles)
  87. try:
  88. cust_rules = config_context['filter']['forward']
  89. for af in [ 4, 6 ]:
  90. if af not in cust_rules:
  91. continue
  92. if type (cust_rules[af]) != list:
  93. raise ValueError ("nftables:filter:forward:%d in config context expected to be a list!" % af)
  94. fp['rules'][af] = cust_rules[af]
  95. except KeyError:
  96. pass
  97. return fp
  98. def generate_nat_policy (roles, config_context):
  99. np = {
  100. 4 : {},
  101. 6 : {},
  102. }
  103. # Any custom rules?
  104. cc_nat = config_context.get ('nat')
  105. if cc_nat:
  106. for chain in ['output', 'prerouting', 'postrouting']:
  107. if chain not in cc_nat:
  108. continue
  109. for af in [ 4, 6 ]:
  110. if str (af) in cc_nat[chain]:
  111. np[4][chain] = cc_nat[chain][str (af)]
  112. return np