资料简介:如果你有以下痛苦:
1、使用默认docker0桥接方式;
2、修改防火墙规则的话,使用手动修改配置;
3、并且修改时候还得计算来源端口,防止重复端口使用户登陆错误容器;
4、并当容器意外重启,内网ip变化后还得修改规则
那么你可以看看本文了,对你这些痛处都有解决方法。
目前docker容器设置访问规则的话,就2个方法
1、在docker容器创建的时候,使用-p来设置
2、在容器运行中,获取容器的ip,然后在宿主机的iptables力通过nat链做dnat设置
我之前一直使用第2个方法,但随着我docker项目的增加(目前我这里研发使用docker的容器做测试机),防火墙的访问规则设置起来十分麻烦,并且之前规划没有弄好,容器的网络还是默认的docker0桥接方式,这样容器一挂或者异常问题、docker daemon重启,都会导致容器的ip变更,变更后就得修改防火墙策略,十分的麻烦。
为了解决这个问题,我开发了2个程序,1个是持久化固定容器ip,另外一个是智能防火墙,下面是关于智能防火墙功能的介绍。
一、介绍
1、编写语言
python
2、运行环境
容器需要使用我之前写的持久化固定ip方式来创建
需要额外安装的python模块
etcd
docker
nmap
3、基本宿主机防火墙(包含filter链与nat链)
默认在/root/firewall里有个基础的宿主机防火墙,里面包含filter链与nat链,我的防火墙程序先获取这个文件,然后在从etcd里获取各容器的防火墙结合后是新的规则,如下面是我的
[root@docker-test3 firewall]# cat /root/firewall/iptables_base.txt*filter:INPUT DROP [0:0]:FORWARD ACCEPT [0:0]:OUTPUT ACCEPT [1:83]-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT-A INPUT -p icmp -j ACCEPT-A INPUT -i lo -j ACCEPT-A INPUT -i em1 -j ACCEPT-A INPUT -i ovs1 -j ACCEPT#forllow is room network-A INPUT -s 117.121.x.0/24 -p tcp -m multiport --dports 50020 -j ACCEPT-A INPUT -p tcp -j REJECT --reject-with tcp-reset-A FORWARD -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK RST -m limit --limit 1/sec -j ACCEPTCOMMIT# Completed on Fri Dec 6 10:59:13 2013*nat:PREROUTING ACCEPT [2:269]:POSTROUTING ACCEPT [1739:127286]:OUTPUT ACCEPT [1739:127286]:DOCKER - [0:0]-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER-A POSTROUTING -s 172.16.0.0/16 ! -d 172.16.0.0/16 -j MASQUERADE-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKERCOMMIT
其中50020是ssh端口,117.121.x.0/24是允许的网段,x的意思保密,不让你们看我的网络。
4、代码
#!/usr/bin/env python#-*- coding: utf-8 -*-#author:Deng Lei#email: dl528888@gmail.comimport osimport sysimport argparseimport etcdimport timeimport socket, struct, fcntlfrom docker import Clientimport subprocessimport shutilimport nmapdef get_local_ip(iface = 'em1'):sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)sockfd = sock.fileno()SIOCGIFADDR = 0x8915ifreq = struct.pack('16sH14s', iface, socket.AF_INET, 'x00'*14)try:res = fcntl.ioctl(sockfd, SIOCGIFADDR, ifreq)except:return Noneip = struct.unpack('16sH2x4s8x', res)[2]return socket.inet_ntoa(ip)def docker_container_all():docker_container=docker_client.containers(all=True)container_name=[]container_stop_name=[]for i in docker_container:container_name.append(i['Names'])for b in container_name:for c in b:container_stop_name.append(c)return container_stop_namedef docker_container_run():docker_container=docker_client.containers()container_name=[]container_stop_name=[]for i in docker_container:container_name.append(i['Names'])for b in container_name:for c in b:container_stop_name.append(c[1::])return container_stop_nameif __name__ == "__main__":#follow is help infop = argparse.ArgumentParser(description='It is userful tool to modify docker container firewall')p.add_argument("container_name",help="list local docker container name")p.add_argument("-l","--list",help="show container firewall rules",action="store_true")p.add_argument("-a","--add",help="add container firewall rules",action="store_true")p.add_argument("-r","--rm",help="rm container firewall rules")p.add_argument("-m","--mode",choices=["internal","external"],help="set container firewall mode")p.add_argument("-s","--source",help="source ip view container firewall rules")p.add_argument("-sp","--sport",help="source port view container firewall rules")p.add_argument("-d","--dest",help="destination ip container firewall rules")p.add_argument("-dp","--dport",help="destination port view container firewall rules")p.add_argument("-pm","--portmode",choices=["dynamic","manual"],help="set container port mode")p.add_argument("-e","--effect",help="effect container firewall rules",action="store_true")p.add_argument("-ap","--addip",help="add external ip to container")p.add_argument("-rp","--rmip",help="rm external ip to container")args = p.parse_args()local_ip=get_local_ip('ovs1')docker_etcd_key='/app/docker/'etcd_client=etcd.Client(host='127.0.0.1', port=4001)docker_client = Client(base_url='unix://var/run/docker.sock', version='1.15', timeout=10)docker_container_all_name=docker_container_all()portmode='manual'container_ip=''#get container ipr = etcd_client.read('%s%s'%(docker_etcd_key,local_ip), recursive=True, sorted=True)for child in r.children:if child.dir is not True and args.container_name in child.key and 'firewall' not in child.key:container_ip=eval(child.value)['Container_ip']if len(container_ip) == 0 and args.container_name != "all":print 'This container:%s info is not in etcd!'%args.container_namesys.exit(1)if '/'+args.container_name not in docker_container_all_name and args.container_name != "all":print 'local host docker is not container:%s!'%args.container_namesys.exit(1)if args.list:try:now_firewall_rule=etcd_client.read('%s%s/firewall/nat-%s'%(docker_etcd_key,local_ip,args.container_name)).valueexcept KeyError:print 'This container:%s is not firewall rule!'%args.container_namesys.exit(1)if len(now_firewall_rule) >0:now_firewall_rule=eval(now_firewall_rule)print 'Follow is container:%s firewall rule!'%args.container_namefor i in now_firewall_rule:print ielse:print 'This container:%s is not firewall rule!'%args.container_namesys.exit(1)if args.portmode=="dynamic":try:now_port=etcd_client.read('%s%s/firewall/now_port'%(docker_etcd_key,local_ip)).valueexcept KeyError:now_port='40000'now_port=int(now_port) + 1key='%s%s/firewall/now_port'%(docker_etcd_key,local_ip)etcd_client.write(key,now_port)portmode=args.portmodeelif args.portmode=="manual":if len(args.sport)>0:now_port=args.sportelse:print 'no input source port'key='%s%s/firewall/now_port'%(docker_etcd_key,local_ip)etcd_client.write(key,now_port)#add docker container firewall ruleif args.add:if args.mode:if args.source:if args.source == "all":source_ip='0.0.0.0/0.0.0.0'else:source_ip=args.sourceif args.portmode=="dynamic":sport=now_portelse:sport=args.sportif args.dport:dport=args.dportelse:print 'please input dest port!This port is container local port!'sys.exit(1)try:now_id=len(eval(etcd_client.read('%s%s/firewall/nat-%s'%(docker_etcd_key,local_ip,args.container_name)).value))except KeyError:now_id='0'except SyntaxError:now_id='0'now_id = int(now_id) + 1if args.mode=="internal":msg={'Id':now_id,'Mode':args.mode,'Container_name':args.container_name,'Source_ip':source_ip,'Port_mode':portmode,'Source_port':'%s'%sport,'Local_port':dport,'Container_ip':container_ip}else:if args.dest:msg={'Id':now_id,'Mode':args.mode,'Container_name':args.container_name,'Source_ip':source_ip,'Destination_ip':args.dest,'Port_mode':portmode,'Source_port':'%s'%sport,'Local_port':dport,'Container_ip':container_ip}else:print 'please input destination ip'sys.exit(1)#add rule to iptablestry:now_firewall_rule=etcd_client.read('%s%s/firewall/nat-%s'%(docker_etcd_key,local_ip,args.container_name)).valuenow_firewall_rule=eval(now_firewall_rule)except KeyError:now_firewall_rule=[]except SyntaxError:now_firewall_rule=[]for i in now_firewall_rule:if msg['Local_port'] == i['Local_port'] and msg['Source_ip'] == i['Source_ip'] and msg['Mode'] == i['Mode'] and msg['Container_name'] == i['Container_name'] and msg['Source_port'] == i['Source_port']:print 'This rule had exist!'sys.exit(1)now_firewall_rule.append(msg)key='%s%s/firewall/nat-%s'%(docker_etcd_key,local_ip,args.container_name)etcd_client.write(key,now_firewall_rule)for i in now_firewall_rule:print i#del exist firewall ruleif args.rm:try:now_info=eval(etcd_client.read('%s%s/firewall/nat-%s'%(docker_etcd_key,local_ip,args.container_name)).value)except KeyError:print 'This Container:%s is not firewall rule!'%args.container_namesys.exit(1)except SyntaxError:print 'This container:%s is not firewall rule!'%args.container_namesys.exit(1)old_id=[i['Id'] for i in now_info]if args.rm != 'all':if int(args.rm) not in old_id:print 'you input rule id %s is not exit!'%args.rmsys.exit(1)for i in now_info:if int(args.rm) == i['Id']:now_info.remove(i)print 'Follow is container_name:%s new firewall rule!'%args.container_namefor i in now_info:print ikey='%s%s/firewall/nat-%s'%(docker_etcd_key,local_ip,args.container_name)etcd_client.write(key,now_info)sys.exit(0)else:now_info=''key='%s%s/firewall/nat-%s'%(docker_etcd_key,local_ip,args.container_name)etcd_client.write(key,now_info)print 'This container_name:%s is not firewall rule!'%args.container_namesys.exit(0)#effect container firewall ruleif args.effect:#check firewall filter existconfig_dir='/root/firewall'iptables_config='iptables_base.txt'if os.path.exists(config_dir) is False:os.mkdir(config_dir)if os.path.isfile('%s/%s'%(config_dir,iptables_config)) is False:print 'no found base iptables config in %s/%s!'%(config_dir,iptables_config)sys.exit(1)docker_container_run=docker_container_run()etcd_exist_firewall=[]if args.container_name != "all":container_name=args.container_nametry:now_info=eval(etcd_client.read('%s%s/firewall/nat-%s'%(docker_etcd_key,local_ip,args.container_name)).value)msg=[]msg.append('#follow is container:%s firewall rulen'%args.container_name)for i in now_info:if 'Destination_ip' not in i:text='-A DOCKER -s %s ! -i ovs2 -p tcp -m tcp --dport %s -j DNAT --to-destination %s:%s'%(i['Source_ip'],i['Source_port'],i['Container_ip'].split('/')[0],i['Local_port'])msg.append('%sn'%text)else:text='-A DOCKER -s %s -d %s ! -i ovs2 -p tcp -m tcp --dport %s -j DNAT --to-destination %s:%s'%(i['Source_ip'],i['Destination_ip'],i['Source_port'],i['Container_ip'].split('/')[0],i['Local_port'])msg.append('%sn'%text)except SyntaxError:msg=''#wirte container firewall ruleiptables_new_config='iptables_nat_%s.txt'%args.container_namef=open('%s/%s'%(config_dir,iptables_new_config),'w')for i in msg:f.write(i)f.close()else:r = etcd_client.read('%s%s/firewall'%(docker_etcd_key,local_ip), recursive=True, sorted=True)for child in r.children:if child.dir is not True and 'nat' in child.key and child.key.split('/')[-1].split('nat-')[-1] in docker_container_run:#etcd_exist_firewall.append(child.key.split('/')[-1].split('nat-')[-1])try:now_info=eval(etcd_client.read(child.key).value)msg=[]msg.append('#follow is container:%s firewall rulen'%child.key.split('/')[-1].split('nat-')[-1])for i in now_info:if 'Destination_ip' not in i:text='-A DOCKER -s %s ! -i ovs2 -p tcp -m tcp --dport %s -j DNAT --to-destination %s:%s'%(i['Source_ip'],i['Source_port'],i['Container_ip'].split('/')[0],i['Local_port'])msg.append('%sn'%text)else:text='-A DOCKER -s %s -d %s ! -i ovs2 -p tcp -m tcp --dport %s -j DNAT --to-destination %s:%s'%(i['Source_ip'],i['Destination_ip'],i['Source_port'],i['Container_ip'].split('/')[0],i['Local_port'])msg.append('%sn'%text)except SyntaxError:msg=''#wirte container firewall ruleiptables_new_config='iptables_nat_%s.txt'%child.key.split('/')[-1].split('nat-')[-1]f=open('%s/%s'%(config_dir,iptables_new_config),'w')for i in msg:f.write(i)f.close()#get now all container firewall ruleall_firewall_file=[]for parent,dirnames,filenames in os.walk(config_dir):for filename in filenames:if 'iptables_nat' in filename:all_firewall_file.append(os.path.join(parent,filename))#get iptables base file linecount = len(open('%s/%s'%(config_dir,iptables_config),'rU').readlines())modify_post=int(count)-1f=open('%s/%s'%(config_dir,iptables_config),'r+')flist=f.readlines()flist[modify_post]=''f=openfor i in all_firewall_file:f=open(i)try:container_text=f.read()finally:f.close()flist.append(container_text)flist.append('COMMITn')f=open('%s/temp_iptables.txt'%config_dir,'w')for i in flist:f.write(i)f.close()#apply new firewall ruleshutil.copy('%s/temp_iptables.txt'%config_dir,'/etc/sysconfig/iptables')#restart firewallfirewall_status=((subprocess.Popen("systemctl restart iptables &>>/dev/null && echo 0 || echo 1",shell=True,stdout=subprocess.PIPE)).stdout.readlines()[0]).strip('n')if firewall_status != "0":print 'firewall rule has problem!'sys.exit(1)else:print 'config firewall rule is success!'sys.exit(0)if args.addip:if '/' not in args.addip:print 'please input ip:netmask!'sys.exit(1)external_ip=args.addip.split('/')[0]external_ip_netmask=args.addip.split('/')[1]#nmap ip exist!nm = nmap.PortScanner()nmap_result=nm.scan(external_ip,'60020')['nmap']['scanstats']['uphosts']if int(nmap_result) == 1:print 'you input ip:%s is online!'%external_ipsys.exit(1)try:now_ip=eval(etcd_client.read('%s%s/external_ip/%s'%(docker_etcd_key,local_ip,external_ip)).value)if now_ip['Container_name'] != args.container_name:print 'this is external ip:%s is has used by container:%s.if you want to use it again,please delete this key:%s.'%(args.addip,now_ip['Container_name'],'%s%s/external_ip/%s'%(docker_etcd_key,local_ip,external_ip))sys.exit(1)except KeyError:pass
新闻热点
疑难解答