使用Python自动化运维ipset与iptables
配置文件
host-rules:
- name: normal_computing
conf: # 正式员工使用的研发运算机规则
type: ip
need_create_ipset: true
need_create_iptables: true
hosts: # 主机列表
- 192.168.1.2
- 192.168.1.4
- 172.31.86.213
- 192.168.1.5
input: # INPUT方向规则
- dport: 22,5600
peer: # INPUT方向规则的来源
- type: ipset
value: normal_computing
- type: ipset
value: win_jump_server
- type: ip
value: 8.8.8.8
output: # OUTPUT方向规则
- peer:
- type: ipset
value: network_monitor_subnet
- peer:
- type: ipset
value: domain_names
- peer:
- type: ipset
value: normal_computing
- peer:
- type: ipset
value: outsourcing_computing
- peer:
- type: ipset
value: cfs
- dport: 22
peer:
- type: ipset
value: normal_computing
- name: network_monitor_subnet
conf:
type: ip,port
need_create_ipset: true
need_create_iptables: false
hosts:
- name: 192.168.100.1
port: tcp:8888
- name: outsourcing_computing
conf: # 外包员工使用的研发运算机规则
type: ip
need_create_ipset: true
need_create_iptables: true
hosts: # 主机列表
- 192.168.1.11
input: # INPUT方向规则
- dport: 22,5600
peer: # INPUT方向规则的来源
- type: ipset
value: normal_computing
- type: ipset
value: win_jump_server
- name: cadence_lic_hosts
conf: # c家lic
type: ip
need_create_ipset: false
need_create_iptables: true
hosts:
- 172.31.86.213
input: # INPUT方向规则
- dport: 3000,5280
peer: # INPUT方向规则的来源
- type: ipset
value: normal_computing
- type: ipset
value: outsourcing_computing
- type: ipset
value: n_gate_subnets
- name: svn_hosts
conf:
type: ip
need_create_ipset: false
need_create_iptables: true
hosts:
- 192.168.1.2
input: # INPUT方向规则
- dport: 1690:1697,1699
peer: # INPUT方向规则的来源
- type: ipset
value: normal_computing
- dport: 1698
peer: # INPUT方向规则的来源
- type: ipset
value: outsourcing_computing
- name: n_gate_subnets
conf:
type: net
need_create_ipset: true
need_create_iptables: false
hosts:
- 172.16.0.1/24
- 172.16.1.1/24
- name: win_jump_server
conf:
type: ip
need_create_ipset: true
need_create_iptables: false
hosts:
- 1.1.1.1
- 1.1.1.2
- name: jump_server
conf:
type: net
need_create_ipset: true
need_create_iptables: false
hosts:
- 2.2.2.0/24
- 2.2.3.0/24
- name: cfs
conf:
type: ip,port
need_create_ipset: true
need_create_iptables: false
hosts:
- name: 1.2.3.4
port: tcp:111
- name: 1.2.3.4
port: tcp:2049
- name: domain_names
conf:
type: dns,port
need_create_ipset: true
need_create_iptables: false
hosts:
- name: mirrors.tencent.com
port: tcp:80
- name: mirrors.tecentyun.com
port: tcp:443
生成脚本
"""
Author: wanlinwang
Date: 24-Aug-2022 19:00-23:00
Description: 自动化维护iptables与ipset
"""
import yaml
import tempfile
from netifaces import interfaces, ifaddresses, AF_INET
import subprocess
import re
import filecmp
def ip4_addresses():
ip_list = []
for interface in interfaces():
# 有些接口没有IP,使用try跳过它。
try:
for link in ifaddresses(interface)[AF_INET]:
ip=link['addr']
#去掉本地地址,去掉192.开头的私有地址。
if ip.startswith('192.') or ip == '127.0.0.1':
continue
ip_list.append(ip)
except:
pass
return ip_list
def dig_domainname_to_ips(domain_name):
entries = subprocess.run(['dig', '+short', domain_name], stdout=subprocess.PIPE)
return entries.stdout.decode('utf-8')
def check_if_ip(ip_str):
if re.match(r"^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$", ip_str):
return True
else:
return False
def create_ipset(set_name, entry_list, set_type, write_file):
"""
生成ipset配置文件,应用命令如下:
ipset restore -f ./ipset.conf --exist
"""
# print('entry_list', entry_list)
if set_type == 'dns,port':
set_type = 'ip,port'
rst_list = []
for entry in entry_list:
ip_list = dig_domainname_to_ips(entry['name']).split('\n')
ip_list.sort()
for item in ip_list:
# 只添加是ip的条目
if check_if_ip(item):
rst_list.append(item + "," + entry['port'])
entry_list = rst_list
elif set_type == 'ip,port':
rst_list = []
for entry in entry_list:
rst_list.append(entry['name'] + "," + entry['port'])
entry_list = rst_list
set_tmp_name = set_name + "_tmp"
print(f"""
####################################################
create {set_name} hash:{set_type} family inet hashsize 1024 maxelem 65536
create {set_tmp_name} hash:{set_type} family inet hashsize 1024 maxelem 65536
destroy {set_tmp_name}
create {set_tmp_name} hash:{set_type} family inet hashsize 1024 maxelem 65536\
""", file=write_file)
for entry in entry_list:
print(f"add {set_tmp_name} {entry}", file=write_file)
print(f"""\
swap {set_tmp_name} {set_name}
destroy {set_tmp_name}
####################################################
""", file=write_file)
def generate_iptables_entry(direction, ip_type, ips, ipset_dict, ports=None):
entry = ['-A']
match_set_arg = ''
if direction == 'input':
entry.append('INPUT')
if ip_type.startswith('ipset'):
match_set_arg = 'src'
elif direction == 'output':
entry.append('OUTPUT')
if ip_type.startswith('ipset'):
match_set_arg = 'dst'
if ipset_dict[ips] == 'ip,port':
match_set_arg = 'dst,dst'
else:
print(f"Error with {direction}")
entry.append(f'-p tcp')
if ip_type == 'ip':
entry.append(f'-s {ips}')
elif ip_type.startswith('ipset'):
entry.append(f'-m set --match-set {ips} {match_set_arg}')
else:
print(f'Error with ip_type {ip_type}')
# if ip_type == 'ip':
# entry.append(f'-s {ips}')
# elif ip_type.startswith('ipset'):
# ip_type = ip_type.replace('ipset,')
# if ip_type == 'ip':
# entry.append(f'-m set --match-set {ips} {match_set_arg}')
# elif ip_type == 'ip,port':
# entry.append(f'-m set --match-set {ips} dst,dst')
# else:
# print(f'Error with ip_type {ip_type}')
if ports:
if ip_type == 'ipset,ip,port':
raise 'Conflict with ipset'
entry.append(f'--dport {ports}')
entry.append(f'-j ACCEPT')
return ' '.join(entry)
if __name__ == "__main__":
ipset_conf_tmp = tempfile.NamedTemporaryFile(mode='w+', prefix='ipset.conf_', dir='./', delete=False)
iptables_conf_tmp = tempfile.NamedTemporaryFile(mode='w+', prefix='iptables_', dir='./', delete=False)
current_host_ips = ip4_addresses()
print("current host ip", current_host_ips)
# 将默认开通的iptables条目先写上。
with open("./config.yml", 'r') as yml:
conf = yaml.safe_load(yml)
# print(json.dumps(conf,indent=2))
# print(conf['host-rules'])
ipset_type_dict = dict()
for host_rule in conf['host-rules']:
# print(json.dumps(host_rule, indent=2))
host_rule_name = host_rule['name']
host_rule_conf = host_rule['conf']
host_rule_conf_type = host_rule_conf['type']
# 步骤一:首先完成ipset的创建。Done
if 'need_create_ipset' in host_rule_conf and host_rule_conf['need_create_ipset']:
create_ipset(host_rule_name, host_rule_conf['hosts'], host_rule_conf_type, ipset_conf_tmp)
if host_rule_conf_type == 'dns,port':
host_rule_conf_type = 'ip,port'
ipset_type_dict[host_rule_name] = host_rule_conf_type
for host_rule in conf['host-rules']:
host_rule_name = host_rule['name']
host_rule_conf = host_rule['conf']
# 步骤二:然后判断本机属于哪个rule下面,将对应的rule都生成一遍。TODO
if 'need_create_iptables' in host_rule_conf and host_rule_conf['need_create_iptables']:
for current_ip in current_host_ips:
# print(host_rule_conf)
# print(host_rule_conf['hosts'])
if 'hosts' not in host_rule_conf:
print(f'hosts not in {host_rule_name}, please check.')
continue
if current_ip in host_rule_conf['hosts']:
# 分析文件条目
# 分析input的
for direction in ['input', 'output']:
if direction not in host_rule_conf:
print(f'{direction} not in {host_rule_name}, please check.')
else:
for d in host_rule_conf[direction]:
# print('direction', d)
dports = None
if 'dport' in d:
dports = d['dport']
if 'peer' not in d:
print(f'{direction} peer not in {host_rule_name}, please check.')
continue
else:
for item in d['peer']:
ip_type = item['type']
value = item['value']
# 生成iptables INPUT条目
# print("=============")
print(generate_iptables_entry(direction, ip_type, value, ipset_type_dict, dports), file=iptables_conf_tmp)
# 步骤三:最后再将default的DROP规则写上。TODO
# 步骤四:生成好ipset.conf与iptables两个文件,与生产环境的文件做对比,
# 如有变则应用它,并将日志写到日志文件里。TODO
ipset_conf_tmp.close()
iptables_conf_tmp.close()
if not filecmp.cmp(ipset_conf_tmp.name, 'ipset.conf'):
# ipset.conf有更新,执行更新操作
pass
if not filecmp.cmp(iptables_conf_tmp.name, 'iptables'):
# iptables文件有更新,执行更新操作
pass
Enjoy Reading This Article?
Here are some more articles you might like to read next: