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

Posted

技术标签:

【中文标题】如何查找未使用的 Amazon EC2 安全组【英文标题】:How to find Unused Amazon EC2 Security groups 【发布时间】:2014-09-01 08:34:59 【问题描述】:

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

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

【问题讨论】:

My Kingdom 的答案可以完全回答这个问题,对于可以分配 SG 且不涉及的长期非实例对象(RDS、ELB、ALB)没有例外全选,然后删除“可怕的周末破坏者”方法。 :) 清理未使用的sg,进入ec2 sg的控制台,选择所有安全组并操作删除,这样会显示没有使用的sg,只删除那些sg。 【参考方案1】:

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

使用较新的 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

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

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)

【讨论】:

@Erik 是的,我只有一个区域,AWS 脚本通过环境变量设置了它们的主区域。我有兴趣查看此脚本的多区域版本。 安全组也可能被 ELB 使用。此命令将列出默认区域中 ELB 引用的安全组 ID 的 uniq 集:aws elb describe-load-balancers --query 'LoadBalancerDescriptions[*].SecurityGroups[*]' --output text | tr '\t' '\n' | sort | uniq 一个 EC2 安全组也可能被 RDS 实例使用。此命令将列出默认区域中 RDS 实例使用的安全组 ID:aws rds describe-db-security-groups --query 'DBSecurityGroups[*].EC2SecurityGroups[*].EC2SecurityGroupId' --output text | tr '\t' '\n' | sort | uniq 您也可以使用aws ec2 describe-network-interfaces --query 'NetworkInterfaces[*].Groups[*].GroupId' --output text| tr '\t' '\n' | sort | uniq 来描述网络接口。 不错的答案。查询提示:将最后一个元素放在括号中,使用文本输出时每行得到一个值,无需tr 命令。例如,--query 'SecurityGroups[].[GroupId]' 与 '--query 'SecurityGroups[].GroupId'`【参考方案2】:

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

注意:根据@andrewlorien 的评论,这不适用于所有类型的 AWS 服务。

【讨论】:

虽然我必须同意,但使用“全选 + 删除”通常并不是一个好习惯。 如果你不确定它是否会起作用,你可以创建一个虚拟安全组并附加一些东西,尝试删除它,看看它不会让你。 您不需要实际确认删除,在弹出窗口中会显示哪些可以删除(孤儿)哪些不能删除。然后您可以按取消,然后删除孤立的。 我不明白的是:如果 AWS 控制台可以在您执行此可怕操作时提供此信息,那么他们为什么不通过 API 分享如何做同样的事情?它不像这不是棕色田野环境中可能需要的东西...... 勇敢:: 去做【参考方案3】:

这是用 boto(AWS 的 Python 开发工具包)编写的示例代码,用于根据关联的实例数列出安全组。

您也可以使用此逻辑在命令行中获得相同的结果

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

【讨论】:

简单又好用!谢谢 嗯,是的,但是 elbs 呢? 另请注意,这仅包括正在运行的实例。您也不能删除链接到已停止实例的 SG。 这会忽略来自 RDS 等服务的接口。 RDS 拥有实例,但您拥有 ENI。我认为 ElasticSearch 和 ELB 的工作方式类似,不会出现在这个脚本中 如果安全组被任何ecs服务使用怎么办?【参考方案4】:

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

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

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

    获取与以下每个服务关联的所有安全组的列表,并将它们从表中排除: EC2 实例 EC2 弹性负载均衡器 AWS RDS 实例 AWS OpsWorks(不应按亚马逊删除) 默认安全组(不能删除) ElastiCache

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

    最后,我显示剩余组的组 ID、组名称和描述。这些是需要审核和/或删除的“未使用”组。我发现实例和弹性负载均衡器 (ELB) 之间的 SG 经常相互引用。在删除交叉引用和删除安全组之前,最好进行一些手动调查以确保它们确实没有被使用。但我的脚本至少将其简化为更易于管理的东西。

注意事项: 1. 您将要创建一个文件来存储您的 MySQL 主机、用户名和密码,并将 $DBCONFIG 变量指向它。它的结构应该是这样的:

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

如果您觉得这很有用或有任何 cmets、修复或增强功能,请告诉我。

这是脚本。

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

在其他函数中,ScoutSuite 和 Prowler 都报告未使用的 EC2 安全组。两者都是开源的。

【讨论】:

【参考方案6】:

打印没有当前实例的安全组的组 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\t1".format(sg.id, sg.name))

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

例如

print ("0\t1\t2 instances".format(sg.id, sg.name, len(sg.instances())))

【讨论】:

【参考方案7】:

使用 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 

【讨论】:

【参考方案8】:

连接到网络接口的 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

【讨论】:

【参考方案9】:

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

【讨论】:

【参考方案10】:

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

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

    我得到了所有 EC2 安全组 -> all_secgrp 我得到了所有 EC2 实例 -> all_instances 对于每个实例,我将所有 SecurityGroups 附加到它
      我从 all_secgrp 中删除了这些 SecurityGroup 中的每一个(因为已附加)
    对于每个 SecurityGroup,我检查与任何 NetworkInterfaces 的关联(使用 filter 函数并使用 security-group-id 进行过滤)
      如果未找到关联,我将安全组从all_secgrp 中删除

附上你可以看到一个sn-p的代码。不要抱怨效率,但如果你愿意,可以尝试优化它。

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  

【讨论】:

【参考方案11】:

如果您的安全组在规则中引用了其他安全组,这是一个难题。如果是这样,您将不得不解决 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【参考方案12】:

我正在搜索相同的信息。 如何查找未附加到任何资源的所有安全组?我发现了这个: 使用 AWS 配置规则“EC2_SECURITY_GROUP_ATTACHED_TO_ENI”,我得到了一个检查列表,检查非默认安全组是否附加到 Amazon Elastic Compute Cloud (EC2) 实例或弹性网络接口 (ENI)。如果安全组未与 EC2 实例或 ENI 关联,则该规则返回 NON_COMPLIANT。

【讨论】:

【参考方案13】:

这是一个非常古老的问题,我敢肯定还有更多方法可以给这只 AWS 猫换皮,但这是我在 bash 中的解决方案(您需要 jq 才能工作):

REGION="eu-west-1"

SGLIST=$(aws ec2 describe-security-groups --query 'SecurityGroups[*].GroupId' | jq -r .[])

echo $SGLIST | xargs -n1 | while read SG; do  [ "$(aws ec2 describe-network-interfaces --filters Name=group-id,Values=$SG --region $REGION | jq .NetworkInterfaces)" != '[]' ] || echo $SG; done

请记住将 REGION 替换为您正在使用的任何区域。 第一步是获取安全组列表。 然后我们检查每个安全组是否有与之关联的网络接口——这不仅限于 EC2 实例,它会检查任何具有网络接口(LB、RDS 等)的东西。 参考见here。

【讨论】:

以上是关于如何查找未使用的 Amazon EC2 安全组的主要内容,如果未能解决你的问题,请参考以下文章

Amazon EC2 IAM 策略:仅限修改单个安全组

如何在ec2元数据中查找安全组ID?

缓存amazon ec2元属性是否安全

在 Windows 中使用 PuTTY SSH 到 Amazon EC2 实例

如何在 Amazon EC2 容器服务 (ECS) 中使用不安全的 docker 注册表?

AWS NTP 服务器的安全组