孤立したセキュリティグループを特定して、クリーンアップして取り除くことができる方法を探しています。誰もが未使用のセキュリティグループを発見する方法を知っていますか。
コンソールまたはコマンドラインツールのどちらでも機能します(LinuxおよびOSXマシンでのコマンドラインツールの実行)。
回答:
注:これはEC2でのセキュリティの使用のみを考慮し、RDSなどの他のサービスは考慮しません。EC2の外部で使用されるセキュリティグループを含めるには、さらに作業を行う必要があります。良い点は、関連付けられている1つのサービスと関連するサービスがないと、アクティブなセキュリティグループを簡単に削除できない(場合によっては不可能である)ことです。
新しい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)
aws elb describe-load-balancers --query 'LoadBalancerDescriptions[*].SecurityGroups[*]' --output text | tr '\t' '\n' | sort | uniq
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
して、ネットワークインターフェイスを説明することもできます。
EC2コンソールですべてのセキュリティグループを選択して、アクション-> [セキュリティグループの削除]を押すと、ポップアップが表示され、インスタンス、他のセキュリティグループ、またはネットワークインターフェイスに接続されているセキュリティグループを削除できないことを通知します。削除できるセキュリティグループが一覧表示されます。つまり、未使用のセキュリティグループ:)
これは、関連付けられているインスタンスの数に対してセキュリティグループを一覧表示するboto(Python SDK for AWS)で記述されたサンプルコードです。
このロジックを使用して、コマンドラインでも同様に取得できます。
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」を使用して、以下を実行しました。
すべてのEC2セキュリティグループのリストを取得します。ローカルホスト上のaws_security_groupsと呼ばれるMySQLデータベースの「グループ」と呼ばれるテーブルにグループID、グループ名、および説明を保存します。見つかったグループの総数はユーザーに報告されます。
次の各サービスに関連付けられているすべてのセキュリティグループのリストを取得し、それらをテーブルから除外します。 )ElastiCache
各サービスについて、除外が完了した後、テーブルに残っているグループの数を報告します。
注:1. MySQLホスト、ユーザー名、パスワードを保存するファイルを作成し、$ DBCONFIG変数にそれを指定します。次のように構成する必要があります。
[mysql]
host=your-mysql-server-host.com
user=your-mysql-user
password=your-mysql-user-password
これが有用であるか、コメント、修正、または機能強化がある場合は、お知らせください。
これがスクリプトです。
#!/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
現在のインスタンスを持たないセキュリティグループのグループ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())))
node.js AWS SDKを使用して、使用中のセキュリティグループをAWSが削除できないことを確認できます。すべてのグループを削除しようとするだけでエラーを適切に処理するスクリプトを作成しました。これは、クラシックVPCとモダン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 }
他の機能の中でも、ScoutSuiteとProwlerはどちらも未使用のEC2セキュリティグループを報告します。どちらもオープンソースです。
ネットワークインターフェイスに接続されている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
AWSマーケットプレイスには、これをはるかに簡単にするツールがあります。簡単に削除できるようにアタッチ/デタッチされたグループが表示されますが、VPCフローログとセキュリティグループルールが比較され、使用中または未使用のSGルールも表示されます。AWSはこれを行うためにELKスタックソリューションを投稿しましたが、それは途方もなく複雑でした。
これがツールで、私が取り組んだ免責事項です。しかし、私はあなたがすべてそれが適切であることを願っています:https : //www.piasoftware.net/single-post/2018/04/24/VIDEO-Watch-as-we-clean-up-EC2-security-groups-in-just -数分
残念ながら、選択した回答は必要なほど正確ではありません(理由を調査しようとしましたが、実装することを選択しました)。
ALLをチェックしNetworkInterfaces
、への添付ファイルを探すとSecurityGroup
、部分的な結果が得られます。のみをチェックするとEC2Instances
、部分的な結果も返されます。
それが問題への私のアプローチです:
all_secgrp
all_instances
filter
関数を使用して、それを使用してフィルタリングしますsecurity-group-id
)。
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
ルール内の他のセキュリティグループを参照するセキュリティグループがある場合、これは難しい問題です。その場合、自明ではない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))