Як знайти невикористані групи безпеки Amazon EC2


93

Я намагаюся знайти спосіб визначити групи-сироти, щоб я міг їх прибрати і позбутися. Хтось знає про спосіб виявлення невикористаних груп безпеки.

Буде працювати через консоль або за допомогою інструментів командного рядка (Запуск інструментів командного рядка на машинах Linux та OSX).


4
«Моє царство» за відповідь, яка повністю відповідає на це питання, без винятків для довгоживучих нестандартних об’єктів (RDS, ELB, ALB), яким можуть бути призначені SG, і не передбачає «вибрати все, а потім видалити» scarybad weekend -руйнівний підхід. :)
Джессі Адельман,

Відповіді:


78

Примітка: це стосується лише використання безпеки в 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
@Erik Так, у мене є лише один регіон, і сценарії AWS мають свій домашній регіон, встановлений за допомогою змінних середовища. Мені було б цікаво переглянути багаторегіональну версію цього сценарію.
Рей

1
ви можете додати --filter для вашого vpc, щоб вам не потрібно було бачити інші
vpc

2
Група безпеки також може використовуватися ОВР. Ця команда перелічить унікальний набір ідентифікаторів груп безпеки, на які посилаються ELB у регіоні за замовчуванням:aws elb describe-load-balancers --query 'LoadBalancerDescriptions[*].SecurityGroups[*]' --output text | tr '\t' '\n' | sort | uniq
astletron

2
Група безпеки EC2 також може використовуватися екземпляром RDS. Ця команда перелічить ідентифікатори групи безпеки, що використовуються екземплярами RDS у регіоні за замовчуванням:aws rds describe-db-security-groups --query 'DBSecurityGroups[*].EC2SecurityGroups[*].EC2SecurityGroupId' --output text | tr '\t' '\n' | sort | uniq
aharden

2
Ви також aws ec2 describe-network-interfaces --query 'NetworkInterfaces[*].Groups[*].GroupId' --output text| tr '\t' '\n' | sort | uniqможете просто описати мережеві інтерфейси.
Джонатан,

63

Якщо ви виберете всі свої групи безпеки на консолі EC2, а потім натисніть дії -> Видалити групи безпеки, з’явиться спливаюче вікно, що ви не можете видалити групи безпеки, які приєднані до екземплярів, інших груп безпеки або мережевих інтерфейсів, перелічить групи безпеки, які ви можете видалити; тобто невикористані групи безпеки :)


15
Хоча я повинен погодитися, використання "вибрати все + видалити", як правило, не є хорошою звичкою.
Бальміпур,

3
Якщо ви не впевнені, чи це спрацює, ви можете просто створити фіктивну групу безпеки і приєднати до неї щось, спробуйте видалити її і переконайтеся, що це вам не дозволить.
NLail

2
Вам не потрібно фактично підтверджувати видалення, у спливаючому вікні воно покаже вам розподіл, які можна видалити (сирота), а які ні. Потім можна натиснути кнопку скасувати, а потім видалити сироту.
rjarmstrong

4
Що я не отримую, це так: якщо консоль AWS може запропонувати цю інформацію, коли ви робите цей scary.maneuver, чому б їм не поділитися, як зробити те саме через API? Це не так, це не те, що, ймовірно, потрібно в середовищі коричневих полів ...
Джессі Адельман,

1
будь мужнім :: зроби це
zanuka

29

Це зразок коду, написаний у програмі boto (Python SDK для AWS), щоб перерахувати групу безпеки за кількістю випадків, з якими вона пов'язана.

Ви можете використовувати цю логіку, щоб отримати те саме в командному рядку

Кодекс Бото

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
ну так, але що за ліби?
Ілля

Також зауважте, що сюди входять лише запущені екземпляри. Ви також не можете видалити SG, який пов'язаний із зупиненим екземпляром.
AgDude

6
Це ігнорує інтерфейси таких служб, як RDS. RDS володіє екземпляром, але ви володієте ENI. Я думаю, що ElasticSearch та ELB працюють однаково і не з'являться з цим сценарієм
rajat banerjee

6

Приблизно через рік неаудитованого використання я визнав за необхідне перевірити свої групи безпеки AWS EC2 та очистити застарілі, невикористані групи.

Це було непросте завдання, яке було виконати через веб-графічний інтерфейс, тому я звернувся до інтерфейсу клієнта AWS, щоб полегшити завдання. Я знайшов початок, як це зробити на StackOverflow, але це було далеко не завершено. Тож я вирішив написати власний сценарій. Я використовував AWS CLI, MySQL та деякі "Bash-foo", щоб виконати наступне:

  1. Отримайте список усіх груп безпеки EC2. Я зберігаю ідентифікатор групи, ім'я групи та опис у таблиці під назвою “groups” у базі даних MySQL, яка називається aws_security_groups на localhost. Загальна кількість знайдених груп повідомляється користувачеві.

  2. Отримайте список усіх груп безпеки, пов’язаних із кожною з наступних служб, і виключіть їх із таблиці: EC2 Istance EC2 Elastic Load Balancers AWS RDS Екземпляри AWS OpsWorks (не слід видаляти на Amazon) Групи безпеки за замовчуванням (Неможливо видалити ) ElastiCache

Для кожної послуги я повідомляю підрахунок кількості груп, що залишились у таблиці після завершення виключення.

  1. Нарешті, я відображаю ідентифікатор групи, назву групи та опис груп, які залишились. Це “невикористані” групи, які необхідно перевірити та / або видалити. Я виявив, що SG між екземплярами та еластичними балансирами навантаження (ELB) часто посилаються один на одного. Найкращою практикою є проведення ручного дослідження, щоб переконатися, що вони справді не використовуються, перш ніж видаляти перехресні посилання та видаляти групи безпеки. Але мій сценарій принаймні зводить це до чогось, що можна управляти.

ПРИМІТКИ: 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

Приклад бото, що друкує ідентифікатори та імена груп лише груп безпеки, які не мають поточних екземплярів.

Він також показує, як вказати, який регіон вас стосується.

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 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 }


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

На жаль, обрана відповідь не настільки точна, як мені потрібно (я намагався дослідити, чому, але віддав перевагу її реалізації).
Якщо я перевіряю ВСЕ NetworkInterfaces, шукаючи вкладення до будь-якого SecurityGroup, це дає мені часткові результати. Якщо я перевіряю лише EC2Instances, це також повертає часткові результати.

Отже, це мій підхід до проблеми:

  1. Я отримую ВСІ групи безпеки EC2 -> all_secgrp
  2. Я отримую ВСІ екземпляри EC2 -> all_instances
  3. Для кожного екземпляра я отримую всі групи безпеки, приєднані до нього
    1. Я видаляю з all_secgrp кожну з цих груп безпеки (оскільки додається)
  4. Для кожної групи безпеки я перевіряю зв'язок з будь-якими мережевими інтерфейсами (використовуючи 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
олександр
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.