development

사용하지 않는 Amazon EC2 보안 그룹을 찾는 방법

big-blog 2020. 10. 26. 08:11
반응형

사용하지 않는 Amazon EC2 보안 그룹을 찾는 방법


고아 보안 그룹을 확인하여 정리하고 제거 할 수있는 방법을 찾으려고합니다. 사용하지 않는 보안 그룹을 검색하는 방법을 아는 사람이 있습니까?

콘솔 또는 명령 줄 도구를 통해 작동합니다 (Linux 및 OSX 시스템에서 명령 줄 도구 실행).


참고 : 이것은 RDS와 같은 다른 서비스가 아닌 EC2의 보안 사용만을 고려합니다. 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

그런 다음 그것을 모아 두 목록을 비교하고 마스터 목록에서 사용되지 않는 것을 확인하십시오.

 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)

EC2 콘솔에서 모든 보안 그룹을 선택한 다음 작업-> 보안 그룹 삭제를 누르면 인스턴스, 다른 보안 그룹 또는 네트워크 인터페이스에 연결된 보안 그룹을 삭제할 수 없음을 알리는 팝업이 나타납니다. 삭제할 수있는 보안 그룹이 나열됩니다. 즉, 사용되지 않는 보안 그룹 :)


연결된 인스턴스 수에 대해 보안 그룹을 나열하기 위해 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

약 1 년 동안 감사를받지 않고 사용한 후 AWS EC2 보안 그룹을 감사하고 사용하지 않는 레거시 그룹을 정리해야한다는 것을 알게되었습니다.

이것은 웹 GUI를 통해 수행하기 어려운 작업 이었으므로 작업을 더 쉽게 만들기 위해 AWS CLI를 살펴 보았습니다. StackOverflow에서이 작업을 수행하는 방법에 대한 시작을 찾았지만 아직 완료되지 않았습니다. 그래서 저는 제 대본을 쓰기로했습니다. AWS CLI, MySQL 및 일부 "Bash-foo"를 사용하여 다음을 수행했습니다.

  1. 모든 EC2 보안 그룹 목록을 가져옵니다. 나는 localhost의 aws_security_groups라는 MySQL 데이터베이스에있는“groups”라는 테이블에 group-id, group-name 및 설명을 저장합니다. 발견 된 총 그룹 수가 사용자에게보고됩니다.

  2. 다음 서비스 각각에 연결된 모든 보안 그룹의 목록을 가져 와서 테이블에서 제외합니다. EC2 Istances EC2 Elastic Load Balancers AWS RDS 인스턴스 AWS OpsWorks (Amazon별로 제거 할 수 없음) 기본 보안 그룹 (삭제할 수 없음) ) ElastiCache

각 서비스에 대해 제외가 완료된 후 테이블에 남아있는 그룹 수를보고합니다.

  1. 마지막으로 남은 그룹에 대한 그룹 ID, 그룹 이름 및 설명을 표시합니다. 감사 및 / 또는 삭제해야하는 "사용되지 않는"그룹입니다. 인스턴스와 ELB (Elastic Load Balancer) 사이의 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

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 }

현재 인스턴스가없는 보안 그룹 의 그룹 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))

To confirm which security groups are still being used you should reverse or remove the if len(sg.instances()) == 0 test and print the len(sg.instances()) value out.

E.g.

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

Among other functions, both ScoutSuite and Prowler report unused EC2 Security Groups. Both are open source.


To the SGs attached to the network interfaces:

By name:

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

By id:

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

There's a tool in the AWS marketplace that makes this a lot easier. It shows you which groups are attached/detached for easy deletion, but it also compares your VPC Flow Logs against the security group rules and shows you which SG rules are in use or unused. AWS posted an ELK-stack solution to do this, but it was ridiculously complex.

Here's the tool, and a disclaimer that I worked on it. But I hope you all find it pertinent: https://www.piasoftware.net/single-post/2018/04/24/VIDEO-Watch-as-we-clean-up-EC2-security-groups-in-just-a-few-minutes


Unfortunately the chosen answer is not as accurate as I need (I've tried to investigate the why, but I've preferred to implement it).
If I check ALL NetworkInterfaces, looking for attachments to any SecurityGroup, It gets me partial results. If I check only on EC2Instances, it gets me back partial results as well.

So that's my approach to the problem:

  1. I get ALL EC2 SecurityGroups -> all_secgrp
  2. I get ALL EC2 Instances -> all_instances
  3. For each Instance, I get all SecurityGroups attached to it
    1. I remove from all_secgrp each of these SecurityGroup (because attached)
  4. For each SecurityGroup, I check an association with any NetworkInterfaces (using the filter function and filtering using that security-group-id)
    1. IF no association is found, I remove the security-group from all_secgrp

Attached you can see a snippet of code. Don't complain for efficiency, but try to optimize it if you want.

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  

This is a difficult problem, if you have security groups that reference other security groups in the rules. If so, you'll have to resolve DependencyErrors, which is not trivial.

If you are only using IP addresses, then this solution will work, after you create a boto3 client:

# 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))

참고URL : https://stackoverflow.com/questions/24685508/how-to-find-unused-amazon-ec2-security-groups

반응형