Comment trouver les groupes de sécurité Amazon EC2 inutilisés


93

J'essaie de trouver un moyen de déterminer les groupes de sécurité orphelins afin que je puisse nettoyer et m'en débarrasser. Quelqu'un connaît-il un moyen de découvrir les groupes de sécurité inutilisés?

Soit via la console, soit avec les outils de ligne de commande fonctionnera (Exécution des outils de ligne de commande sur les machines Linux et OSX).


3
My Kingdom pour une réponse qui répond entièrement à cette question, sans exception pour les objets non-Instance à longue durée de vie (RDS, ELB, ALB) auxquels des SG peuvent leur être attribués, et n'implique pas `` sélectionner tout, puis supprimer '' effrayant week-end approche -destroyer. :)
Jesse Adelman

Réponses:


77

Remarque: cela ne prend en compte que l'utilisation de la sécurité dans EC2, pas d'autres services comme RDS. Vous devrez faire plus de travail pour inclure les groupes de sécurité utilisés en dehors d'EC2. La bonne chose est que vous ne pouvez pas facilement (peut-être même pas possible) supprimer des groupes de sécurité actifs si vous manquez un associé avec un autre service.

En utilisant le nouvel outil AWS CLI, j'ai trouvé un moyen simple d'obtenir ce dont j'ai besoin:

Tout d'abord, obtenez une liste de tous les groupes de sécurité

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

Ensuite, obtenez tous les groupes de sécurité liés à une instance, puis dirigés vers sortensuite uniq:

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

Ensuite, rassemblez-les et comparez les 2 listes et voyez ce qui n'est pas utilisé dans la liste 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 Oui, je n'ai qu'une seule région et les scripts AWS ont leur région d'origine définie via des variables d'environnement. Je serais intéressé de voir une version multirégionale de ce script.
Ray

1
vous pouvez ajouter un --filter pour votre vpc afin de ne pas avoir à voir d'autres vpc sg par défaut
shadowbq

2
Un groupe de sécurité peut également être utilisé par un ELB. Cette commande répertorie l'ensemble uniq des ID de groupe de sécurité référencés par les ELB dans la région par défaut:aws elb describe-load-balancers --query 'LoadBalancerDescriptions[*].SecurityGroups[*]' --output text | tr '\t' '\n' | sort | uniq
astletron

2
Un groupe de sécurité EC2 peut également être utilisé par une instance RDS. Cette commande répertorie les ID de groupe de sécurité utilisés par les instances RDS dans la région par défaut:aws rds describe-db-security-groups --query 'DBSecurityGroups[*].EC2SecurityGroups[*].EC2SecurityGroupId' --output text | tr '\t' '\n' | sort | uniq
aharden

2
Vous pouvez également utiliser aws ec2 describe-network-interfaces --query 'NetworkInterfaces[*].Groups[*].GroupId' --output text| tr '\t' '\n' | sort | uniqpour simplement décrire les interfaces réseau.
Jonathan

61

Si vous sélectionnez tous vos groupes de sécurité dans la console EC2, puis appuyez sur actions -> Supprimer les groupes de sécurité, une fenêtre contextuelle apparaîtra vous indiquant que vous ne pouvez pas supprimer les groupes de sécurité attachés à des instances, à d'autres groupes de sécurité ou à des interfaces réseau. listera les groupes de sécurité que vous pouvez supprimer; c'est à dire les groupes de sécurité inutilisés :)


15
Bien que je sois d'accord, utiliser "tout sélectionner + supprimer" n'est généralement pas une bonne habitude.
Balmipour

3
Si vous ne savez pas si cela fonctionnera, vous pouvez simplement créer un groupe de sécurité factice et y attacher quelque chose, essayez de le supprimer et voyez qu'il ne vous le permettra pas.
NLail

2
Vous n'avez pas besoin de confirmer la suppression, dans la fenêtre contextuelle, il vous montrera une ventilation de ceux qui peuvent être supprimés (orphelins) et ceux qui ne le peuvent pas. Vous pouvez ensuite appuyer sur Annuler puis supprimer les orphelins.
rjarmstrong

4
Ce que je n'obtiens pas est le suivant: si la console AWS peut offrir ces informations lorsque vous effectuez cette effrayante.manœuvre, pourquoi ne partagent-elles pas comment faire la même chose via l'API? Ce n'est pas comme si ce n'était pas quelque chose qui est probablement nécessaire dans les environnements de terrain brun ...
Jesse Adelman

1
be brave :: do it
zanuka

29

Il s'agit de l'exemple de code écrit en boto (Python SDK for AWS) pour répertorier le groupe de sécurité par rapport au nombre d'instances auxquelles il est associé.

Vous pouvez également utiliser cette logique pour obtenir la même chose en ligne de commande

Code Boto

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

Production

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

Sympa et facile! Merci
Chris Koston

6
eh bien, oui, mais qu'est-ce que les elbes?
Ilja

Notez également que cela inclut uniquement les instances en cours d'exécution. Vous ne pouvez pas non plus supprimer un SG lié à une instance arrêtée.
AgDude le

6
Cela ignore les interfaces de services comme RDS. RDS possède l'instance, mais vous possédez l'ENI. Je pense qu'ElasticSearch et ELB fonctionnent de la même manière et
n'apparaîtraient

6

Après environ un an d'utilisation non auditée, j'ai jugé nécessaire d'auditer mes groupes de sécurité AWS EC2 et de nettoyer les groupes hérités et inutilisés.

C'était une tâche ardue à effectuer via l'interface graphique Web, alors j'ai regardé l'AWS CLI pour faciliter la tâche. J'ai trouvé un début sur la façon de faire cela chez StackOverflow, mais c'était loin d'être terminé. J'ai donc décidé d'écrire mon propre scénario. J'ai utilisé l'AWS CLI, MySQL et certains «Bash-foo» pour effectuer les opérations suivantes:

  1. Obtenez une liste de tous les groupes de sécurité EC2. Je stocke l'identifiant du groupe, le nom du groupe et la description dans un tableau appelé «groupes» dans une base de données MySQL appelée aws_security_groups sur l'hôte local. Le nombre total de groupes trouvés est signalé à l'utilisateur.

  2. Obtenez une liste de tous les groupes de sécurité associés à chacun des services suivants et excluez-les du tableau: EC2 Istances EC2 Elastic Load Balancers Instances AWS RDS AWS OpsWorks (ne doit pas être supprimé par Amazon) Groupes de sécurité par défaut (ne peut pas être supprimé ) ElastiCache

Pour chaque service, je signale un décompte du nombre de groupes restants dans le tableau une fois l'exclusion terminée.

  1. Enfin, j'affiche l'identifiant du groupe, le nom du groupe et la description des groupes restants. Ce sont les groupes «inutilisés» qui doivent être audités et / ou supprimés. J'ai constaté que les SG entre les instances et les Elastic Load Balancers (ELB) se réfèrent souvent les uns aux autres. Il est recommandé d'effectuer une enquête manuelle pour s'assurer qu'ils ne sont vraiment pas utilisés avant de supprimer les références croisées et de supprimer les groupes de sécurité. Mais mon script réduit au moins cela à quelque chose de plus gérable.

REMARQUES: 1. Vous voudrez créer un fichier pour stocker votre hôte MySQL, votre nom d'utilisateur et votre mot de passe et pointer la variable $ DBCONFIG dessus. Il devrait être structuré comme ceci:

[mysql]
host=your-mysql-server-host.com
user=your-mysql-user
password=your-mysql-user-password
  1. Vous pouvez changer le nom de la base de données si vous le souhaitez - assurez-vous de changer la variable $ DB dans le script

Faites-moi savoir si vous trouvez cela utile ou avez des commentaires, des correctifs ou des améliorations.

Voici le script.

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

Et voici le sql pour créer la base de données.

-- 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 exemple de boto imprimant uniquement les ID et les noms de groupe des groupes de sécurité qui n'ont pas d'instances actuelles.

Il montre également comment spécifier la région qui vous intéresse.

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

Pour confirmer quels groupes de sécurité sont encore utilisés, vous devez inverser ou supprimer le if len(sg.instances()) == 0test et imprimer lelen(sg.instances()) valeur.

Par exemple

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

3

À l'aide du kit SDK AWS node.js, je peux confirmer qu'AWS ne vous autorise pas à supprimer les groupes de sécurité en cours d'utilisation. J'ai écrit un script qui essaie simplement de supprimer tous les groupes et gère gracieusement les erreurs. Cela fonctionne pour les VPC classiques et modernes. Le message d'erreur peut être vu ci-dessous.

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

Aux SG connectés aux interfaces réseau:

De nom:

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

Par identifiant:

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

0

Il existe un outil sur le marché AWS qui facilite grandement les choses. Il vous montre quels groupes sont attachés / détachés pour une suppression facile, mais il compare également vos journaux de flux VPC aux règles de groupe de sécurité et vous montre quelles règles SG sont utilisées ou inutilisées. AWS a publié une solution ELK-stack pour ce faire, mais c'était ridiculement complexe.

Voici l'outil et une clause de non-responsabilité indiquant que j'ai travaillé dessus. Mais j'espère que vous le trouverez tous pertinent: https://www.piasoftware.net/single-post/2018/04/24/VIDEO-Watch-as-we-clean-up-EC2-security-groups-in-just -quelques minutes


0

Malheureusement, la réponse choisie n'est pas aussi précise que nécessaire (j'ai essayé d'enquêter sur le pourquoi, mais j'ai préféré l'implémenter).
Si je coche TOUT NetworkInterfaces, à la recherche de pièces jointes SecurityGroup, cela me donne des résultats partiels. Si je vérifie uniquementEC2Instances , cela me récupère également des résultats partiels.

Voilà donc mon approche du problème:

  1. J'obtiens TOUS les groupes de sécurité EC2 -> all_secgrp
  2. Je reçois TOUTES les instances EC2 -> all_instances
  3. Pour chaque instance, je reçois tous les SecurityGroups qui lui sont attachés
    1. Je supprime de all_secgrp chacun de ces SecurityGroup (car attaché)
  4. Pour chaque SecurityGroup, je vérifie une association avec toutes les NetworkInterfaces (en utilisant la filterfonction et en filtrant en utilisant cettesecurity-group-id )
    1. SI aucune association n'est trouvée, je supprime le groupe de sécurité de all_secgrp

En pièce jointe, vous pouvez voir un extrait de code. Ne vous plaignez pas de l'efficacité, mais essayez de l'optimiser si vous le souhaitez.

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

Il s'agit d'un problème difficile si vous avez des groupes de sécurité qui référencent d'autres groupes de sécurité dans les règles. Si tel est le cas, vous devrez résoudre DependencyErrors, ce qui n'est pas trivial.

Si vous n'utilisez que des adresses IP, cette solution fonctionnera après avoir créé 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))

Cela ne couvrira pas les SG utilisés par RDS
alexandernst
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.