如何查找未使用的Amazon EC2安全组

时间:2014-07-10 20:17:29

标签: amazon-web-services amazon-ec2

我试图找到一种方法来确定孤立的安全组,以便我可以清理并清除它们。有没有人知道发现未使用的安全组的方法。

通过控制台或命令行工具都可以工作(在Linux和OSX机器上运行命令行工具)。

11 个答案:

答案 0 :(得分:66)

注意:这仅考虑EC2中的安全性使用,而不考虑RDS等其他服务。您需要做更多工作才能包含在EC2之外使用的安全组。好处是,如果您错过了一个与其他服务相关联的服务,您就不能轻易(甚至可能无法)删除活动安全组。

使用较新的AWS CLI工具,我找到了一种简单的方法来获得我需要的东西:

首先,获取所有安全组的列表

 aws ec2 describe-security-groups --query 'SecurityGroups[*].GroupId'  --output text | tr '\t' '\n'

然后将所有安全组绑定到实例,然后通过管道传输到sort,然后uniq

aws ec2 describe-instances --query 'Reservations[*].Instances[*].SecurityGroups[*].GroupId' --output text | tr '\t' '\n' | sort | uniq

然后将它们放在一起并比较2个列表,看看主列表中没有使用的内容:

 comm -23  <(aws ec2 describe-security-groups --query 'SecurityGroups[*].GroupId'  --output text | tr '\t' '\n'| sort) <(aws ec2 describe-instances --query 'Reservations[*].Instances[*].SecurityGroups[*].GroupId' --output text | tr '\t' '\n' | sort | uniq)

答案 1 :(得分:41)

如果您在EC2控制台中选择了所有安全组,请按操作 - &gt;删除安全组,将出现一个弹出窗口,告知您无法删除附加到实例,其他安全组或网络接口的安全组,它将列出您可以删除的安全组;即未使用的安全组:)

答案 2 :(得分:22)

这是用boto(适用于AWS的Python SDK)编写的示例代码,用于根据与之关联的实例数列出安全组。

您可以使用此逻辑在命令行中获取相同的内容

Boto代码

import boto
ec2 = boto.connect_ec2()
sgs = ec2.get_all_security_groups()
for sg in sgs:
    print sg.name, len(sg.instances())

<强>输出

Security-Group-1 0
Security-Group-2 1
Security-Group-3 0
Security-Group-4 3

答案 3 :(得分:3)

使用node.js AWS SDK我可以确认AWS不允许您删除正在使用的安全组。我写了一个脚本,它只是试图删除所有组并优雅地处理错误。这适用于经典和现代VPC。错误信息可以在下面看到。

Err { [DependencyViolation: resource sg-12345678 has a dependent object]
  message: 'resource sg-12345678 has a dependent object',
  code: 'DependencyViolation',
  time: Mon Dec 07 2015 12:12:43 GMT-0500 (EST),
  statusCode: 400,
  retryable: false,
  retryDelay: 30 }

答案 4 :(得分:2)

经过大约一年的未经审核使用后,我发现有必要审核我的AWS EC2安全组并清理未使用的旧组。

这是通过Web GUI执行的艰巨任务,因此我查看了AWS CLI以使任务更容易。我在StackOverflow上找到了如何做到这一点的开始,但它远未完成。所以我决定编写自己的脚本。我使用AWS CLI,MySQL和一些“Bash-foo”来执行以下操作:

  1. 获取所有EC2安全组的列表。 我将group-id,group-name和description存储在localhost上名为aws_security_groups的MySQL数据库中名为“groups”的表中。找到的组总数将报告给用户。

  2. 获取与以下每项服务相关联的所有安全组的列表,并将其从表中排除: EC2 Istances EC2弹性负载平衡器 AWS RDS实例 AWS OpsWorks(不应每个亚马逊删除) 默认安全组(无法删除) ElastiCache

  3. 对于每项服务,我会在排除完成后报告表格中剩余的组数。

    1. 最后,我显示了剩下的组的group-id,group-name和description。这些是需要审核和/或删除的“未使用”组。我发现实例和Elastic Load Balancers(ELB)之间的SG经常互相引用。在删除交叉引用和删除安全组之前,最佳做法是进行一些手动调查以确保它们确实未被使用。但是我的剧本至少可以将其简化为可管理的东西。
    2. 注意: 1.您需要创建一个文件来存储MySQL主机,用户名和密码,并将$ DBCONFIG变量指向它。它的结构应该是这样的:

      [mysql]
      host=your-mysql-server-host.com
      user=your-mysql-user
      password=your-mysql-user-password
      
      1. 如果您愿意,可以更改数据库的名称 - 确保更改脚本中的$ DB变量
      2. 如果您认为这有用或有任何评论,修正或改进,请告诉我。

        这是脚本。

        #!/bin/bash
        # Initialize Variables
        DBCONFIG="--defaults-file=mysql-defaults.cnf"
        DB="aws_security_groups"
        SGLOOP=0
        EC2LOOP=0
        ELBLOOP=0
        RDSLOOP=0
        DEFAULTLOOP=0
        OPSLOOP=0
        CACHELOOP=0
        DEL_GROUP=""
        
        # Function to report back # of rows
        function Rows {
            ROWS=`echo "select count(*) from groups" | mysql $DBCONFIG --skip-column-names $DB`
        #   echo -e "Excluding $1 Security Groups.\nGroups Left to audit: "$ROWS
            echo -e $ROWS" groups left after Excluding $1 Security Groups."
        }
        
        
        # Empty the table
        echo -e "delete from groups where groupid is not null" | mysql $DBCONFIG $DB
        
        # Get all Security Groups
        aws ec2 describe-security-groups --query "SecurityGroups[*].[GroupId,GroupName,Description]" --output text > /tmp/security_group_audit.txt
        while IFS=$'\t' read -r -a myArray
        do
            if [ $SGLOOP -eq 0 ];
            then
                VALUES="(\""${myArray[0]}"\",\""${myArray[1]}"\",\""${myArray[2]}"\")"
            else
                VALUES=$VALUES",(\""${myArray[0]}"\",\""${myArray[1]}"\",\""${myArray[2]}"\")"
            fi
            let SGLOOP="$SGLOOP + 1"
        done < /tmp/security_group_audit.txt
        echo -e "insert into groups (groupid, groupname, description) values $VALUES" | mysql $DBCONFIG $DB
        echo -e $SGLOOP" security groups total."
        
        
        # Exclude Security Groups assigned to Instances
        for groupId in `aws ec2 describe-instances --output json | jq -r ".Reservations[].Instances[].SecurityGroups[].GroupId" | sort | uniq`
        do
            if [ $EC2LOOP -eq 0 ];
            then
                DEL_GROUP="'$groupId'"
            else
                DEL_GROUP=$DEL_GROUP",'$groupId'"
            fi
            let EC2LOOP="$EC2LOOP + 1"
        done
        echo -e "delete from groups where groupid in ($DEL_GROUP)" | mysql $DBCONFIG $DB
        Rows "EC2 Instance"
        DEL_GROUP=""
        
        
        # Exclude groups assigned to Elastic Load Balancers
        for elbGroupId in `aws elb describe-load-balancers --output json | jq -c -r ".LoadBalancerDescriptions[].SecurityGroups" | tr -d "\"[]\"" | sort | uniq`
        do
            if [ $ELBLOOP -eq 0 ];
            then
                DEL_GROUP="'$elbGroupId'"
            else
                DEL_GROUP=$DEL_GROUP",'$elbGroupId'"
            fi
            let ELBLOOP="$ELBLOOP + 1"
        done
            echo -e "delete from groups where groupid in ($DEL_GROUP)" | mysql $DBCONFIG $DB
        Rows "Elastic Load Balancer"
        DEL_GROUP=""
        
        
        # Exclude groups assigned to RDS
        for RdsGroupId in `aws rds describe-db-instances --output json | jq -c -r ".DBInstances[].VpcSecurityGroups[].VpcSecurityGroupId" | sort | uniq`
        do
            if [ $RDSLOOP -eq 0 ];
            then
                DEL_GROUP="'$RdsGroupId'"
            else
                DEL_GROUP=$DEL_GROUP",'$RdsGroupId'"
            fi
            let RDSLOOP="$RDSLOOP + 1"
        done
            echo -e "delete from groups where groupid in ($DEL_GROUP)" | mysql $DBCONFIG $DB
        Rows "RDS Instances"
        DEL_GROUP=""
        
        # Exclude groups assigned to OpsWorks
        for OpsGroupId in `echo -e "select groupid from groups where groupname like \"AWS-OpsWorks%\"" | mysql $DBCONFIG $DB`
        do
            if [ $OPSLOOP -eq 0 ];
            then
                DEL_GROUP="'$OpsGroupId'"
            else
                DEL_GROUP=$DEL_GROUP",'$OpsGroupId'"
            fi
            let OPSLOOP="$OPSLOOP + 1"
        done
        echo -e "delete from groups where groupid in ($DEL_GROUP)" | mysql $DBCONFIG $DB
        Rows "OpsWorks"
        DEL_GROUP=""
        
        # Exclude default groups (can't be deleted)
        for DefaultGroupId in `echo -e "select groupid from groups where groupname like \"default%\"" | mysql $DBCONFIG $DB`
        do
            if [ $DEFAULTLOOP -eq 0 ];
            then
                DEL_GROUP="'$DefaultGroupId'"
            else
                DEL_GROUP=$DEL_GROUP",'$DefaultGroupId'"
            fi
            let DEFAULTLOOP="$DEFAULTLOOP + 1"
        done
        echo -e "delete from groups where groupid in ($DEL_GROUP)" | mysql $DBCONFIG $DB
        Rows "Default"
        DEL_GROUP=""
        
        # Exclude Elasticache groups
        for CacheGroupId in `aws elasticache describe-cache-clusters --output json | jq -r ".CacheClusters[].SecurityGroups[].SecurityGroupId" | sort | uniq`
        do
            if [ $CACHELOOP -eq 0 ];
            then
                DEL_GROUP="'$CacheGroupId'"
            else
                DEL_GROUP=$DEL_GROUP",'$CacheGroupId'"
            fi
            let CACHELOOP="$CACHELOOP + 1"
        done
        echo -e "delete from groups where groupid in ($DEL_GROUP)" | mysql $DBCONFIG $DB
        Rows "ElastiCache"
        
        # Display Security Groups left to audit / delete
        echo "select * from groups order by groupid" | mysql $DBCONFIG $DB | sed 's/groupid\t/groupid\t\t/'
        

        这是创建数据库的SQL。

        -- MySQL dump 10.13  Distrib 5.5.41, for debian-linux-gnu (x86_64)
        --
        -- Host:  localhost   Database: aws_security_groups
        -- ------------------------------------------------------
        -- Server version   5.5.40-log
        
        /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
        /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
        /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
        /*!40101 SET NAMES utf8 */;
        /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
        /*!40103 SET TIME_ZONE='+00:00' */;
        /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
        /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
        /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
        /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
        
        --
        -- Table structure for table `groups`
        --
        
        DROP TABLE IF EXISTS `groups`;
        /*!40101 SET @saved_cs_client     = @@character_set_client */;
        /*!40101 SET character_set_client = utf8 */;
        CREATE TABLE `groups` (
          `groupid` varchar(12) DEFAULT NULL,
          `groupname` varchar(200) DEFAULT NULL,
          `description` varchar(200) DEFAULT NULL
        ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
        /*!40101 SET character_set_client = @saved_cs_client */;
        
        --
        -- Dumping data for table `groups`
        --
        
        LOCK TABLES `groups` WRITE;
        /*!40000 ALTER TABLE `groups` DISABLE KEYS */;
        /*!40000 ALTER TABLE `groups` ENABLE KEYS */;
        UNLOCK TABLES;
        /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
        
        /*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
        /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
        /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
        /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
        /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
        /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
        /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
        
        -- Dump completed on 2015-01-27 16:07:44
        

答案 5 :(得分:2)

boto示例打印没有当前实例的安全组的组ID和名称​​

它还显示了如何指定您所关注的区域。

import boto
import boto.ec2
EC2_REGION='ap-southeast-2'
ec2region = boto.ec2.get_region(EC2_REGION)
ec2 = boto.connect_ec2(region=ec2region)
sgs = ec2.get_all_security_groups()
for sg in sgs:
    if len(sg.instances()) == 0:
        print ("{0}\t{1}".format(sg.id, sg.name))

要确认哪些安全组 仍在使用,您应该撤消或删除if len(sg.instances()) == 0测试并打印len(sg.instances())值。

E.g。

print ("{0}\t{1}\t{2} instances".format(sg.id, sg.name, len(sg.instances())))

答案 6 :(得分:1)

在其他功能中,ScoutSuiteProwler都报告未使用的EC2安全组。两者都是开源的。

答案 7 :(得分:0)

AWS市场中有一个工具可以让这更容易。它显示了哪些组被连接/分离以便于删除,但它还会将您的VPC流日志与安全组规则进行比较,并显示正在使用或未使用的SG规则。 AWS发布了一个ELK-stack解决方案来实现这一目标,但它非常复杂。

这是该工具,以及我在其上工作的免责声明。但我希望你们都发现它是相关的: https://www.piasoftware.net/single-post/2018/04/24/VIDEO-Watch-as-we-clean-up-EC2-security-groups-in-just-a-few-minutes

答案 8 :(得分:0)

不幸的是,所选择的答案并不像我需要的那样准确(我已经尝试调查原因,但我更愿意实施它)。 如果我检查所有NetworkInterfaces,查找任何SecurityGroup的附件,它会得到部分结果。如果我只检查EC2Instances,它也会让我得到部分结果。

这就是我解决问题的方法:

  1. 我获得所有EC2安全组 - &gt; all_secgrp
  2. 我获得所有EC2实例 - &gt; all_instances
  3. 对于每个实例,我都会附加所有安全组
    1. 我从all_secgrp中删除了每个SecurityGroup(因为附件)
  4. 对于每个SecurityGroup,我检查与任何NetworkInterfaces的关联(使用filter函数并使用security-group-id进行过滤)
    1. 如果未找到任何关联,我会从all_secgrp
    2. 中删除安全组
  5. 附上你可以看到一段代码。不要抱怨效率,但如果你愿意,可以尝试优化它。

    all_secgrp = list(ec2_connector.security_groups.all())
    all_instances = ec2_connector.instances.all()
    
    for single_instance in all_instances:
        instance_secgrp = ec2_connector.Instance(single_instance.id).security_groups
        for single_sec_grp in instance_secgrp:
            if ec2.SecurityGroup(id=single_sec_grp['GroupId']) in all_secgrp:
                all_secgrp.remove(ec2.SecurityGroup(id=single_sec_grp['GroupId']))
    
    all_secgrp_detached_tmp = all_secgrp[:]
    for single_secgrp in all_secgrp_detached_tmp:
        try:
            print(single_secgrp.id)
            if len(list(ec2_connector.network_interfaces.filter(Filters=[{'Name': 'group-id', 'Values': [single_secgrp.id]}]))) > 0:
                all_secgrp.remove(single_secgrp)
        except Exception:
            all_secgrp.remove(single_secgrp)
    
    return all_secgrp_detached  
    

答案 9 :(得分:0)

如果您有引用规则中其他安全组的安全组,则这是一个难题。如果是这样,则必须解决DependencyErrors,这并非易事。

如果仅使用IP地址,则在创建boto3客户端后,此解决方案将起作用:

grad_w1

答案 10 :(得分:0)

对于连接到网络接口的SG:

按名称:

aws ec2 describe-network-interfaces --output text --query NetworkInterfaces[*].Groups[*].GroupName | tr -d '\r' | tr "\t" "\n" | sort | uniq

通过ID:

aws ec2 describe-network-interfaces --output text --query NetworkInterfaces[*].Groups[*].GroupId | tr -d '\r' | tr "\t" "\n" | sort | uniq