2015年3月12日星期四

docker高级应用之智能添加与修改防火墙规则

本邮件内容由第三方提供,如果您不想继续收到该邮件,可 点此退订
docker高级应用之智能添加与修改防火墙规则  阅读原文»

  • 三日内更新
docker高级应用之智能添加与修改防火墙规则

如果你有以下痛苦:

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(地址http://dl528888.blog.51cto.com/2382721/1616527),另外一个是智能防火墙,下面是关于智能防火墙功能的介绍。

一、介绍

1、编写语言

python

2、运行环境

容器需要使用我之前写的持久化固定ip方式来创建

需要额外安装的python模块

etcd

docker

nmap

3、代码

  #!/usr/bin/env python  #-*- coding: utf-8 -*-  #author:Deng Lei  #email: dl528888@gmail.com  import os  import sys  import argparse  import etcd  import time  import socket, struct, fcntl  from docker import Client  import subprocess  import shutil  import nmap  def get_local_ip(iface = 'em1'):      sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)      sockfd = sock.fileno()      SIOCGIFADDR = 0x8915      ifreq = struct.pack('16sH14s', iface, socket.AF_INET, '\x00'*14)      try:          res = fcntl.ioctl(sockfd, SIOCGIFADDR, ifreq)      except:          return None      ip = 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_name  def 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_name  if __name__ == "__main__":      #follow is help info      p = 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 ip      r = 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_name          sys.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_name          sys.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)).value          except KeyError:              print 'This container:%s is not firewall rule!'%args.container_name              sys.exit(1)          if len(now_firewall_rule) >0:              now_firewall_rule=eval(now_firewall_rule)              print 'Follow is container:%s firewall rule!'%args.container_name              for i in now_firewall_rule:                  print i          else:              print 'This container:%s is not firewall rule!'%args.container_name          sys.exit(1)      if args.portmode=="dynamic":          try:              now_port=etcd_client.read('%s%s/firewall/now_port'%(docker_etcd_key,local_ip)).value          except KeyError:              now_port='40000'          now_port=int(now_port) + 1          key='%s%s/firewall/now_port'%(docker_etcd_key,local_ip)          etcd_client.write(key,now_port)          portmode=args.portmode      elif args.portmode=="manual":          if len(args.sport)>0:              now_port=args.sport          else:              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 rule      if 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.source                  if args.portmode=="dynamic":                      sport=now_port                  else:                      sport=args.sport                  if args.dport:                      dport=args.dport                  else:                      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) + 1                  if 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 iptables                      try:                      now_firewall_rule=etcd_client.read('%s%s/firewall/nat-%s'%(docker_etcd_key,local_ip,args.container_name)).value                      now_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
生产环境-linux-网站被挂木马攻防经历  阅读原文»

生产环境-linux-网站被挂木马攻防经历

安全与方便始终是对立的,然而运维人员忽视系统安全方面的建设,带来的后果将是非常严重的,以下是一台未上线服务器入侵后的攻防经历。

一、出现异常,排查原因

发现异常是通过远端监控脚本发现访问网站时断时续,使用ssh工具连接会经常断掉连接,无法开展工作。

使用其他服务器对另一个网卡ip进行ssh连接,可以登录服务器,初步怀疑网卡异常或者流量异常。

分别使用ifstat、iftop、nethogs查看连接异常网卡流量信息(对几个流量分析工具进行对比,各有千秋):

1使用ifstat

wget http://distfiles.macports.org/ifstat/ifstat-1.1.tar.gz

cdifstat-1.1 ./configuremake make install 都是老套路

ifstat -a加入监控lo

2使用iftop监控那个端口流量

p 可以显示连接端口

3使用nethogs监控每个进程流量

yumrpel

wgethttp://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm

yum install nethogs

nethogs eth0

效果展示ifstat:

wKioL1T_qFqg5-OcAAC16sPBwKA186.jpg

wKiom1T_pz3wpdrpAAHDo9U17lM327.jpg

可以看到服务器流量异常时,流量的变化情况。

效果展示iftop:

wKiom1T_qmmBRmW7AAE_-DFRisI750.jpg

可以看到哪个PID进程导致流量过高

效果展示nethogs:

wKioL1T_rauRkStzAAKRclbUINE734.jpg

二、查找真凶

通过上面分析,造成网站打不开的原因就是有进程大量发送数据包到某个ip(其实是阿里的服务器)的80端口,导致服务器网络阻塞,但是通过以上的工具,发现此木马相当的狡猾,无法发使用的那个进程。

在使用netstatss通过端口获取木马进程失败后(后面才知道netstat、ps等命令已经被木马感染)

可以使用top工具,此木马在大量发包时肯定会造成资源的消耗

通过锁定发现了两个进程有大量的嫌疑:

wKiom1T_pz6Tq7AGAAJRmr8HSS0718.jpg

通过ps命令找到进程执行的目录:

  /usr/bin/sshupdate-bootsystem-insserv  /tmp/GuiBger  

通过持续观察发现时有agent进程一闪而逝,在使用find / -name agent

  # ll /usr/bin/bsd-port/  总用量 1120  -rwxr-xr-x. 1 root root 1135000 12月 25 11:20agent  -rwxr-xr-x. 1 root root       4 12月 25 11:20 agent.conf  -rw-r--r--. 1 root root      69 12月 25 11:50 conf.n  -rw-r--r--. 1 root root       0 10月  9 19:36 getty  

此时,相关的可以进程都找到了,通过测试,在网络阻塞时,删除sshupdate-bootsystem-insservGuiBger两个进程后,网络流量立即正常。而agent则怀疑是与黑客的通信进程,用于接收命令(瞎猜的)或者监控上面连个进程。

三、解决真凶

找到这3个进程并不意味结束,因为他们很可以是开机自启动程序,所以要在找到他们的开机自起的配置文件,我通过一个脚本实现这个功能:

  #!/bin/sh  echo > /tmp/find_init.log  function ergodic(){           forfile in `ls $1`           do                     if[ -d $1"/"$file ] #如果 file存在且是一个目录则为真                              then                              ergodic$1"/"$file                     else                              localpath=$1"/"$file #得到文件的完整的目录                              localname=$file       #得到文件的名字                              #做自己的工作.                              echo  $path                              rootkit_init=`cat$path | grep sshupdate | head -n 1`                              if[ -z $rootkit_init ];then                              echo  "sed -i 's#$rootkit_init##g' $path">>  /tmp/find_init.log                              fi                     fi           done  }  INIT_PATH="/etc/init.d"  ergodic $INIT_PATH  cat /tmp/find_init.log  

这个脚本功能很简单,通过遍历/etc/init.d目录所有文件,使用grep搜索进程名关键词,将含有这几个进程的文件找出来。

结果如下:

  sed -i's#/usr/bin/sshupdate-bootsystem-insserv##g' /etc/init.d/DbSecurityMdt  sed -i's#/usr/bin/sshupdate-bootsystem-insserv##g' /etc/init.d/insserv  

还真有自启动配置,迅速删除之

在删除这个木马命令时会遇到无法删除的问题,这个很简单:

  lsattr /usr/bin/sshupdate-bootsystem-insserv  查看文件的隐藏权限  -------i------e- sshupdate-bootsystem-insserv  发现被限制删除操作了  chattr -i  /usr/bin/sshupdate-bootsystem-insserv  取消影藏权限,然后再删除,完成。  

通过查看数据的表结构,发现木马是从数据库渗透进服务器的,因为没有上线,方便开发测试在密码强度上使用了弱密码,所以被黑客破解了mysql的root账户密码,在mysql注入了木马,一步一步渗透到服务器。数据库方面处理就不详细介绍了:

  1. 检查生产库的表结构,删除多余表。

  2. 然后备份所有生产库。

  3. 停止mysql

  4. 删除datadir目录所有文件

  5. 重启mysql

  6. 导入此前备份数据库

阅读更多内容

没有评论:

发表评论