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


93

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

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


3
我的王国的答案可以完全回答这个问题,对于可以为其分配了SG且不涉及“全选,然后删除”的长寿命非实例对象(RDS,ELB,ALB)无例外驱逐舰的方法。:)
杰西·阿德尔曼

Answers:


77

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

使用更新的AWS CLI工具,我找到了一种获取所需资源的简便方法:

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

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

然后将所有安全组绑定到一个实例,然后通过管道传递给sortthen uniq

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

然后将其放在一起,比较两个列表,然后从主列表中查看未使用的内容:

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
@Erik是的,我只有一个区域,并且AWS脚本通过环境变量设置了其主区域。我希望看到此脚本的多区域版本。

1
您可能想为您的vpc添加--filter,因此您不必查看其他默认vpc sg
shadowbq

2
安全组也可以使用安全组。此命令将列出默认区域中ELB引用的安全组ID的唯一集合:aws elb describe-load-balancers --query 'LoadBalancerDescriptions[*].SecurityGroups[*]' --output text | tr '\t' '\n' | sort | uniq
astletron,2016年

2
RDS实例也可能正在使用EC2安全组。此命令将在默认区域中列出RDS实例使用的安全组ID:aws rds describe-db-security-groups --query 'DBSecurityGroups[*].EC2SecurityGroups[*].EC2SecurityGroupId' --output text | tr '\t' '\n' | sort | uniq
强化

2
您也aws ec2 describe-network-interfaces --query 'NetworkInterfaces[*].Groups[*].GroupId' --output text| tr '\t' '\n' | sort | uniq可以仅用来描述网络接口。
乔纳森

61

如果您在EC2控制台中选择所有安全组,然后按操作->删除安全组,将显示一个弹出窗口,告诉您您无法删除附加到实例,其他安全组或网络接口的安全组。将列出您可以删除的安全组;即未使用的安全组:)


15
我必须同意,使用“全选+删除”通常不是一个好习惯。
Balmipour

3
如果不确定它是否会起作用,您可以创建一个虚拟安全组并将其附加到该组中,然后尝试将其删除,然后发现它不会允许您这样做。
NLail

2
您无需实际确认删除,在弹出窗口中,它将显示您可以删除(孤立)和不能删除的细目。然后,您可以按“取消”,然后删除孤立的那些。
rjarmstrong

4
我不明白的是:如果您在执行Scary.maneuver时,AWS控制台可以提供此信息,为什么他们不通过API分享如何做相同的事情?它不是像这样,在棕地环境中可能不需要……
Jesse Adelman

1
勇敢::做到这一点
zanuka

29

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

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

博托密码

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

好,易于!谢谢
克里斯·科斯顿

6
好吧,是的,但是阿布·埃尔布斯是什么?
伊利亚(Ilja)

另请注意,这仅包括正在运行的实例。您也不能删除链接到已停止实例的SG。
AgDude

6
这会忽略RDS等服务的接口。RDS拥有实例,但您拥有ENI。我认为ElasticSearch和ELB的工作方式相似,并且不会在此脚本中显示
rajat banerjee

6

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

通过Web GUI执行这是一项艰巨的任务,因此我希望通过AWS CLI简化该任务。我在StackOverflow上找到了如何执行此操作的开始,但还远远没有完成。因此,我决定编写自己的脚本。我使用了AWS CLI,MySQL和一些“ Bash-foo”来执行以下操作:

  1. 获取所有EC2安全组的列表。我将组ID,组名和描述存储在本地主机上名为aws_security_groups的MySQL数据库中的表“ groups”中。找到的组总数将报告给用户。

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

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

  1. 最后,我显示剩下的组的组ID,组名称和描述。这些是“未使用”的组,需要对其进行审核和/或删除。我发现实例与弹性负载平衡器(ELB)之间的SG通常相互引用。最佳做法是进行一些手动调查,以确保在删除交叉引用和删除安全组之前,它们确实未被使用。但是我的脚本至少可以将其简化为更易于管理的内容。

注意:1.您将要创建一个文件来存储您的MySQL主机,用户名和密码,然后将$ DBCONFIG变量指向该文件。它的结构应如下所示:

[mysql]
host=your-mysql-server-host.com
user=your-mysql-user
password=your-mysql-user-password
  1. 您可以根据需要更改数据库的名称-确保在脚本中更改$ DB变量。

如果您觉得此功能有用或有任何评论,修复或增强功能,请告诉我。

这是脚本。

#!/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

3

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

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

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())值。

例如

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

3

使用node.js AWS开发工具包,我可以确认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 }


1

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

按名字:

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

按编号:

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

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 -几分钟


0

不幸的是,选择的答案没有我所需要的准确(我试图调查原因,但我更喜欢实施它)。
如果我检查ALL NetworkInterfaces,寻找任何附件SecurityGroup,则得到部分结果。如果我只检查EC2Instances,它也会使我得到部分结果。

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

  1. 我得到所有EC2安全组-> all_secgrp
  2. 我得到所有EC2实例-> all_instances
  3. 对于每个实例,我都将附加所有安全组。
    1. 我从all_secgrp中删除了每个SecurityGroup(因为已附加)
  4. 对于每个SecurityGroup,我检查与任何NetworkInterfaces的关联(使用filter函数并使用进行过滤security-group-id
    1. 如果未找到关联,则从以下位置删除安全组 all_secgrp

附件中您可以看到一段代码。不要抱怨效率,但是如果需要,可以尝试对其进行优化。

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  

0

如果您有引用规则中其他安全组的安全组,这将是一个难题。如果是这样,您将不得不解决DependencyErrors,这并不简单。

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

# pull all security groups from all vpcs in the given profile and region and save as a set
all_sgs = {sg['GroupId'] for sg in client.describe_security_groups()['SecurityGroups']}

# create a new set for all of the security groups that are currently in use
in_use = set()

# cycle through the ENIs and add all found security groups to the in_use set
for eni in client.describe_network_interfaces()['NetworkInterfaces']:
    for group in eni['Groups']:
        in_use.add(group['GroupId'])

unused_security_groups = all_sgs - in_use

for security_group in unused_security_groups:
    try:
        response = client.delete_security_group(GroupId=security_group)
    except ClientError as e:
        if e.response['Error']['Code'] == 'DependencyViolation':
            print('EC2/Security Group Dependencies Exist')
    else:
        print('Unexpected error: {}'.format(e))

这将不包括RDS使用的SG-
亚历山大
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.