Jak znaleźć nieużywane grupy zabezpieczeń Amazon EC2

93

Próbuję znaleźć sposób na określenie osieroconych grup zabezpieczeń, abym mógł je posprzątać i się ich pozbyć. Czy ktoś zna sposób na znalezienie nieużywanych grup zabezpieczeń.

Albo przez konsolę, albo za pomocą narzędzi wiersza poleceń będą działać (Uruchamianie narzędzi wiersza poleceń na komputerach z systemem Linux i OSX).

Promień
źródło
4
Moje Królestwo, aby uzyskać odpowiedź, która w pełni odpowiada na to pytanie, bez wyjątków dla długowiecznych obiektów niebędących instancjami (RDS, ELB, ALB), które mogą mieć przypisane do nich SG, i nie obejmuje `` wybierz wszystko, a następnie usuń '' straszny weekend podejście do niszczyciela. :)
Jesse Adelman

Odpowiedzi:

78

Uwaga: dotyczy to tylko bezpieczeństwa w EC2, a nie innych usług, takich jak RDS. Będziesz musiał wykonać więcej pracy, aby uwzględnić grupy zabezpieczeń używane poza EC2. Dobrą rzeczą jest to, że nie można łatwo (może nawet nie być możliwe) usunięcia aktywnych grup zabezpieczeń, jeśli przegapisz jedną powiązaną z inną usługą.

Korzystając z nowszego narzędzia AWS CLI, znalazłem łatwy sposób na uzyskanie tego, czego potrzebuję:

Najpierw pobierz listę wszystkich grup zabezpieczeń

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

Następnie uzyskać wszystkie grupy zabezpieczeń związane z wystąpieniem, a następnie rurami do sortnastępnie uniq:

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

Następnie złóż to razem i porównaj 2 listy i zobacz, co nie jest używane z listy głównej:

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)
Promień
źródło
1
@Erik Tak, mam tylko jeden region, a skrypty AWS mają swój region macierzysty ustawiany za pomocą zmiennych środowiskowych. Chciałbym zobaczyć wersję tego skryptu obejmującą wiele regionów.
Ray
1
możesz chcieć dodać --filter dla swojego vpc, żeby nie widzieć innych domyślnych vpc sg
shadowbq
2
Grupa bezpieczeństwa może być również używana przez ELB. To polecenie wyświetli zestaw unikalnych identyfikatorów grup bezpieczeństwa, do których odwołują się ELB w regionie domyślnym:aws elb describe-load-balancers --query 'LoadBalancerDescriptions[*].SecurityGroups[*]' --output text | tr '\t' '\n' | sort | uniq
astletron
2
Grupa zabezpieczeń EC2 może być również używana przez instancję RDS. To polecenie wyświetli listę identyfikatorów grup zabezpieczeń używanych przez wystąpienia RDS w regionie domyślnym:aws rds describe-db-security-groups --query 'DBSecurityGroups[*].EC2SecurityGroups[*].EC2SecurityGroupId' --output text | tr '\t' '\n' | sort | uniq
aharden
2
Możesz również użyć aws ec2 describe-network-interfaces --query 'NetworkInterfaces[*].Groups[*].GroupId' --output text| tr '\t' '\n' | sort | uniqtylko do opisania interfejsów sieciowych.
Jonathan
63

Jeśli wybierzesz wszystkie swoje grupy bezpieczeństwa w konsoli EC2, a następnie naciśnij akcje -> Usuń grupy zabezpieczeń, pojawi się wyskakujące okienko z informacją, że nie możesz usunąć grup zabezpieczeń, które są dołączone do instancji, innych grup zabezpieczeń lub interfejsów sieciowych i wyświetli listę grup zabezpieczeń, które możesz usunąć; czyli nieużywane grupy zabezpieczeń :)

NLail
źródło
15
Chociaż muszę się zgodzić, używanie opcji „wybierz wszystko + usuń” zwykle nie jest dobrym nawykiem.
Balmipour
3
Jeśli nie jesteś pewien, czy to zadziała, możesz po prostu utworzyć fikcyjną grupę bezpieczeństwa i dołączyć do niej coś, spróbuj ją usunąć i zobacz, że ci nie pozwoli.
NLail
2
Nie musisz faktycznie potwierdzać usunięcia, w wyskakującym okienku pokaże się zestawienie, które z nich można usunąć (osierocone), a które nie. Możesz następnie nacisnąć anuluj, a następnie usunąć osierocone.
rjarmstrong
4
Nie rozumiem tego: jeśli konsola AWS może zaoferować te informacje podczas wykonywania tego przerażającego manewru, dlaczego nie udostępnią, jak zrobić to samo za pośrednictwem interfejsu API? To nie jest tak, że nie jest to coś, co jest prawdopodobnie potrzebne w środowiskach
Jesse Adelman
1
be brave :: do it
zanuka.
29

To jest przykładowy kod napisany w boto (Python SDK for AWS) w celu zestawienia grupy zabezpieczeń z liczbą wystąpień, z którymi jest powiązana.

Możesz użyć tej logiki, aby uzyskać to samo również w wierszu poleceń

Kod Boto

import boto
ec2 = boto.connect_ec2()
sgs = ec2.get_all_security_groups()
for sg in sgs:
    print sg.name, len(sg.instances())

Wynik

Security-Group-1 0
Security-Group-2 1
Security-Group-3 0
Security-Group-4 3
Naveen Vijay
źródło
Miło i łatwo! Dzięki
Chris Koston
6
no tak, ale co abou elbs?
Ilja
Należy również pamiętać, że dotyczy to tylko uruchomionych instancji. Nie możesz również usunąć SG, który jest połączony z zatrzymaną instancją.
AgDude
6
Ignoruje to interfejsy usług takich jak RDS. RDS jest właścicielem instancji, ale jesteś właścicielem ENI. Myślę, że ElasticSearch i ELB działają podobnie i nie pojawią się z tym skryptem
rajat banerjee
6

Po około roku nieaudytowanego użytkowania stwierdziłem, że konieczne jest przeprowadzenie audytu moich grup bezpieczeństwa AWS EC2 i wyczyszczenie starszych, nieużywanych grup.

To było trudne zadanie do wykonania przez web GUI, więc spojrzałem na AWS CLI, aby to zadanie było łatwiejsze. Znalazłem wskazówki, jak to zrobić, w StackOverflow, ale było to dalekie od ukończenia. Postanowiłem więc napisać własny scenariusz. Użyłem interfejsu wiersza polecenia AWS, MySQL i jakiegoś „Bash-foo”, aby wykonać następujące czynności:

  1. Uzyskaj listę wszystkich grup zabezpieczeń EC2. Przechowuję identyfikator grupy, nazwę grupy i opis w tabeli o nazwie „grupy” w bazie danych MySQL o nazwie aws_security_groups na hoście lokalnym. Całkowita liczba znalezionych grup jest raportowana użytkownikowi.

  2. Uzyskaj listę wszystkich grup zabezpieczeń powiązanych z każdą z następujących usług i wyklucz je z tabeli: EC2 Istances EC2 Elastic Load Balancers AWS RDS Instances AWS OpsWorks (nie powinno być usuwane przez Amazon) Domyślne grupy zabezpieczeń (nie można usunąć ) ElastiCache

Dla każdej usługi podaję liczbę grup pozostawionych w tabeli po zakończeniu wykluczenia.

  1. Na koniec wyświetlam identyfikator grupy, nazwę grupy i opis pozostałych grup. Są to „nieużywane” grupy, które należy poddać audytowi i / lub usunąć. Odkryłem, że SG między instancjami i Elastic Load Balancers (ELB) często odnoszą się do siebie. Najlepszą praktyką jest ręczne zbadanie, aby upewnić się, że nie są one naprawdę używane, przed usunięciem odniesień i grupami zabezpieczeń. Ale mój scenariusz przynajmniej sprowadza to do czegoś łatwiejszego do opanowania.

UWAGI: 1. Będziesz chciał utworzyć plik do przechowywania twojego hosta MySQL, nazwy użytkownika i hasła i wskazać mu zmienną $ DBCONFIG. Powinien mieć następującą strukturę:

[mysql]
host=your-mysql-server-host.com
user=your-mysql-user
password=your-mysql-user-password
  1. Jeśli chcesz, możesz zmienić nazwę bazy danych - upewnij się, że zmieniłeś zmienną $ DB w skrypcie

Daj mi znać, jeśli uznasz to za przydatne lub masz jakieś uwagi, poprawki lub ulepszenia.

Oto skrypt.

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

A oto sql do stworzenia bazy danych.

-- 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
user2962402
źródło
3

Przykład boto drukujący identyfikatory i nazwy grup tylko tych grup zabezpieczeń, które nie mają bieżących wystąpień.

Pokazuje również, jak określić, który region Cię interesuje.

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

Aby potwierdzić, które grupy zabezpieczeń nadal używane, należy cofnąć lub usunąć if len(sg.instances()) == 0test i wydrukować len(sg.instances())wartość.

Na przykład

print ("{0}\t{1}\t{2} instances".format(sg.id, sg.name, len(sg.instances())))
Akira Kurogane
źródło
3

Korzystając z node.js AWS SDK mogę potwierdzić, że AWS nie pozwala na usuwanie grup zabezpieczeń, które są w użyciu. Napisałem skrypt, który po prostu próbuje usunąć wszystkie grupy iz wdziękiem obsługuje błędy. Działa to w przypadku klasycznego i nowoczesnego VPC. Komunikat o błędzie można zobaczyć poniżej.

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 }
Michael Connor
źródło
3

Wśród innych funkcji zarówno ScoutSuite , jak i Prowler zgłaszają nieużywane grupy zabezpieczeń EC2. Oba są open source.

Duża dynia
źródło
1

Do SG podłączonych do interfejsów sieciowych:

Wg nazwy:

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

Według identyfikatora:

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

Na rynku AWS jest narzędzie, które znacznie to ułatwia. Pokazuje, które grupy są dołączone / odłączone w celu łatwego usunięcia, ale także porównuje dzienniki przepływu VPC z regułami grup bezpieczeństwa i pokazuje, które reguły SG są używane lub nie. AWS opublikował rozwiązanie stosu ELK, aby to zrobić, ale było to absurdalnie złożone.

Oto narzędzie i zastrzeżenie, nad którym pracowałem. Ale mam nadzieję, że wszyscy uznacie to za stosowne: https://www.piasoftware.net/single-post/2018/04/24/VIDEO-Watch-as-we-clean-up-EC2-security-groups-in-just -kilka minut

rajat banerjee
źródło
0

Niestety wybrana odpowiedź nie jest tak dokładna, jak potrzebuję (próbowałem zbadać przyczynę, ale wolałem ją wdrożyć).
Jeśli zaznaczę WSZYSTKIE NetworkInterfaces, szukając załączników do jakichkolwiek SecurityGroup, dostaję częściowe wyniki. Jeśli sprawdzę tylko włączone EC2Instances, otrzymam również częściowe wyniki.

Oto moje podejście do problemu:

  1. Otrzymuję WSZYSTKIE grupy zabezpieczeń EC2 -> all_secgrp
  2. Otrzymuję WSZYSTKIE instancje EC2 -> all_instances
  3. Do każdej instancji są dołączone wszystkie SecurityGroups
    1. Usuwam z all_secgrp każdą z tych SecurityGroup (ponieważ jest dołączona)
  4. Dla każdej SecurityGroup sprawdzam powiązanie z dowolnym interfejsem NetworkInterfaces (używając filterfunkcji i filtrując ją security-group-id)
    1. JEŚLI nie zostanie znalezione żadne skojarzenie, usuwam grupę zabezpieczeń z all_secgrp

W załączeniu możesz zobaczyć fragment kodu. Nie narzekaj na wydajność, ale spróbuj ją zoptymalizować, jeśli chcesz.

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  
Echoes_86
źródło
0

Jest to trudny problem, jeśli masz grupy zabezpieczeń, które odwołują się do innych grup zabezpieczeń w regułach. Jeśli tak, będziesz musiał rozwiązać DependencyErrors, co nie jest trywialne.

Jeśli korzystasz tylko z adresów IP, to rozwiązanie zadziała po utworzeniu klienta 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))
jedzenie
źródło
Nie dotyczy to SG używanych przez RDS
alexandernst