Come trovare i gruppi di sicurezza di Amazon EC2 inutilizzati


93

Sto cercando di trovare un modo per determinare i gruppi di sicurezza orfani in modo da poterli ripulire e sbarazzarmi di loro. Qualcuno sa di un modo per scoprire i gruppi di sicurezza inutilizzati.

O tramite la console o con gli strumenti della riga di comando funzioneranno (esecuzione degli strumenti della riga di comando su macchine Linux e OSX).


3
Il mio regno per una risposta che risponda pienamente a questa domanda, senza eccezioni per oggetti non Istanza di lunga durata (RDS, ELB, ALB) a cui possono essere assegnate SG e non coinvolge "seleziona tutto, quindi elimina" scarybad weekend -approccio distruttore. :)
Jesse Adelman

Risposte:


77

Nota: questo considera solo l'uso della sicurezza in EC2, non altri servizi come RDS. Dovrai lavorare di più per includere i gruppi di sicurezza utilizzati al di fuori di EC2. La cosa buona è che non puoi facilmente (potrebbe non essere nemmeno possibile) eliminare i gruppi di sicurezza attivi se perdi uno associato a un altro servizio.

Utilizzando il nuovo strumento AWS CLI, ho trovato un modo semplice per ottenere ciò di cui ho bisogno:

Innanzitutto, ottieni un elenco di tutti i gruppi di sicurezza

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

Quindi ottenere tutti i gruppi di sicurezza legati a un'istanza, quindi convogliato a sortpoi uniq:

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

Quindi mettilo insieme e confronta i 2 elenchi e guarda cosa non viene utilizzato dall'elenco principale:

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 Sì, ho solo una singola regione e gli script AWS hanno la loro regione di origine impostata tramite variabili ambientali. Sarei interessato a vedere una versione multi-regione di questo script.
Ray

1
potresti voler aggiungere un --filter per il tuo vpc in modo da non dover vedere altri vpc predefiniti sg
shadowbq

2
Un gruppo di sicurezza può anche essere utilizzato da un ELB. Questo comando elencherà il set uniq di ID gruppo di sicurezza a cui fanno riferimento gli ELB nella regione predefinita:aws elb describe-load-balancers --query 'LoadBalancerDescriptions[*].SecurityGroups[*]' --output text | tr '\t' '\n' | sort | uniq
astletron

2
Un gruppo di sicurezza EC2 può anche essere utilizzato da un'istanza RDS. Questo comando elencherà gli ID del gruppo di sicurezza utilizzati dalle istanze RDS nella regione predefinita:aws rds describe-db-security-groups --query 'DBSecurityGroups[*].EC2SecurityGroups[*].EC2SecurityGroupId' --output text | tr '\t' '\n' | sort | uniq
aharden

2
Puoi anche usare aws ec2 describe-network-interfaces --query 'NetworkInterfaces[*].Groups[*].GroupId' --output text| tr '\t' '\n' | sort | uniqper descrivere solo le interfacce di rete.
Jonathan

61

Se selezioni tutti i tuoi gruppi di sicurezza nella console EC2, quindi premi azioni -> Elimina gruppi di sicurezza, verrà visualizzato un popup che ti informa che non puoi eliminare i gruppi di sicurezza collegati a istanze, altri gruppi di sicurezza o interfacce di rete e elencherà i gruppi di sicurezza che puoi eliminare; cioè i gruppi di sicurezza inutilizzati :)


15
Anche se devo essere d'accordo, usare "seleziona tutto + elimina" di solito non è una buona abitudine.
Balmipour

3
Se non sei sicuro che funzionerà, puoi semplicemente creare un gruppo di sicurezza fittizio e allegarvi qualcosa, provare a eliminarlo e vedere che non te lo consente.
NLail

2
Non è necessario confermare effettivamente l'eliminazione, nel popup ti mostrerà un'analisi di quali possono essere eliminati (orfani) e quali no. È quindi possibile premere Annulla e quindi eliminare quelli orfani.
rjarmstrong

4
Quello che non capisco è questo: se la console AWS può offrire queste informazioni quando esegui questa manovra spaventosa, perché non condividono come fare la stessa cosa tramite l'API? Non è che questo non sia qualcosa che probabilmente è necessario in ambienti marroni ...
Jesse Adelman

1
sii coraggioso :: fallo
zanuka

29

Questo è il codice di esempio scritto in boto (Python SDK per AWS) per elencare il gruppo di sicurezza in base al numero di istanze a cui è associato.

È possibile utilizzare questa logica per ottenere lo stesso anche nella riga di comando

Codice Boto

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

Produzione

Security-Group-1 0
Security-Group-2 1
Security-Group-3 0
Security-Group-4 3

Bello e facile! Grazie
Chris Koston

6
beh, sì, ma cosa sono i gomiti?
Ilja

Notare inoltre che questo include solo le istanze in esecuzione. Non è nemmeno possibile eliminare un SG che è collegato a un'istanza arrestata.
AgDude

6
Questo ignora le interfacce di servizi come RDS. RDS possiede l'istanza, ma tu sei il proprietario dell'ENI. Penso che ElasticSearch ed ELB funzionino in modo simile e non verrebbero visualizzati con questo script
rajat banerjee

6

Dopo circa un anno di utilizzo non certificato, ho ritenuto necessario controllare i miei gruppi di sicurezza AWS EC2 e ripulire i gruppi legacy non utilizzati.

Questo era un compito arduo da eseguire tramite la GUI Web, quindi ho cercato l'AWS CLI per semplificare l'attività. Ho trovato un inizio su come farlo in StackOverflow, ma era tutt'altro che completo. Così ho deciso di scrivere la mia sceneggiatura. Ho utilizzato AWS CLI, MySQL e alcuni "Bash-foo" per eseguire quanto segue:

  1. Ottieni un elenco di tutti i gruppi di sicurezza EC2. Memorizzo l'ID del gruppo, il nome del gruppo e la descrizione in una tabella chiamata "gruppi" in un database MySQL chiamato aws_security_groups sull'host locale. Il numero totale di gruppi trovati viene segnalato all'utente.

  2. Ottieni un elenco di tutti i gruppi di sicurezza associati a ciascuno dei seguenti servizi ed escludili dalla tabella: Istanze EC2 EC2 Elastic Load Balancer Istanze AWS RDS AWS OpsWorks (non deve essere rimosso per Amazon) Gruppi di sicurezza predefiniti (non possono essere eliminati ) ElastiCache

Per ogni servizio riporto un conteggio del numero di gruppi rimasti nella tabella dopo che l'esclusione è stata completata.

  1. Infine visualizzo l'ID del gruppo, il nome del gruppo e la descrizione per i gruppi rimasti. Questi sono i gruppi "non utilizzati" che devono essere controllati e / o eliminati. Ho scoperto che le SG tra le istanze e gli Elastic Load Balancer (ELB) spesso si riferiscono tra loro. È consigliabile eseguire alcune indagini manuali per assicurarsi che non siano effettivamente in uso prima di rimuovere i riferimenti incrociati ed eliminare i gruppi di sicurezza. Ma il mio copione almeno lo riduce a qualcosa di più gestibile.

NOTE: 1. Dovrai creare un file per memorizzare il tuo host MySQL, nome utente e password e puntare su di esso la variabile $ DBCONFIG. Dovrebbe essere strutturato in questo modo:

[mysql]
host=your-mysql-server-host.com
user=your-mysql-user
password=your-mysql-user-password
  1. È possibile modificare il nome del database se lo si desidera, assicurarsi di modificare la variabile $ DB nello script

Fammi sapere se lo trovi utile o hai commenti, correzioni o miglioramenti.

Ecco il copione.

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

Ed ecco lo sql per creare il database.

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

Un esempio di boto stampa gli ID e nomi dei gruppi solo dei gruppi di protezione che non hanno istanze correnti.

Mostra anche come specificare la regione che ti interessa.

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

Per confermare quali gruppi di sicurezza sono ancora in uso, annullare o rimuovere il if len(sg.instances()) == 0test e stampare il len(sg.instances())valore.

Per esempio

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

3

Utilizzando l'SDK AWS node.js posso confermare che AWS non consente di eliminare i gruppi di sicurezza in uso. Ho scritto uno script che cerca semplicemente di eliminare tutti i gruppi e gestisce con garbo gli errori. Funziona per VPC classico e moderno. Il messaggio di errore può essere visualizzato di seguito.

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

Alle SG collegate alle interfacce di rete:

Per nome:

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

Per id:

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

0

C'è uno strumento nel mercato AWS che lo rende molto più semplice. Ti mostra quali gruppi sono collegati / scollegati per una facile eliminazione, ma confronta anche i tuoi log di flusso VPC con le regole del gruppo di sicurezza e mostra quali regole SG sono in uso o inutilizzate. AWS ha pubblicato una soluzione con stack ELK per farlo, ma era incredibilmente complessa.

Ecco lo strumento e una dichiarazione di non responsabilità su cui ho lavorato. Ma spero che lo troviate pertinente: https://www.piasoftware.net/single-post/2018/04/24/VIDEO-Watch-as-we-clean-up-EC2-security-groups-in-just -pochi minuti


0

Purtroppo la risposta scelta non è così precisa come mi serve (ho provato a indagare sul perché, ma ho preferito implementarla).
Se controllo TUTTO NetworkInterfaces, cercando allegati a qualcuno SecurityGroup, ottengo risultati parziali. Se controllo solo EC2Instances, ottengo anche risultati parziali.

Quindi questo è il mio approccio al problema:

  1. Ottengo TUTTI i SecurityGroup EC2 -> all_secgrp
  2. Ottengo TUTTE le istanze EC2 -> all_instances
  3. Per ogni istanza, ottengo tutti i SecurityGroup collegati ad essa
    1. Rimuovo da all_secgrp ciascuno di questi SecurityGroup (perché allegato)
  4. Per ogni SecurityGroup, controllo un'associazione con qualsiasi NetworkInterfaces (usando la filterfunzione e filtrando usando quella security-group-id)
    1. SE non viene trovata alcuna associazione, rimuovo il gruppo di sicurezza da all_secgrp

In allegato puoi vedere uno snippet di codice. Non lamentarti dell'efficienza, ma cerca di ottimizzarla se lo desideri.

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

Questo è un problema difficile, se disponi di gruppi di sicurezza che fanno riferimento ad altri gruppi di sicurezza nelle regole. In tal caso, dovrai risolvere DependencyErrors, il che non è banale.

Se stai utilizzando solo indirizzi IP, questa soluzione funzionerà dopo aver creato un client 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))

Questo non coprirà gli SG utilizzati da RDS
alexandernst
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.