web-dev-qa-db-fra.com

Comment trouver des groupes de sécurité Amazon EC2 non utilisés

J'essaie de trouver un moyen de déterminer les groupes de sécurité Orphan afin de pouvoir les nettoyer et les éliminer. Est-ce que quelqu'un connaît un moyen de découvrir des groupes de sécurité inutilisés?.

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

62
Ray

Remarque: cela ne concerne 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 être possible) supprimer des groupes de sécurité actifs si vous manquez un service associé w/autre.

À l'aide du nouvel outil AWS CLI, j'ai trouvé un moyen simple d'obtenir ce dont j'ai besoin:

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

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

Ensuite, associez tous les groupes de sécurité à une instance, puis transmettez-les à sort puis uniq:

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

Ensuite, rassemblez-les, 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)
66
Ray

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 vous informant que vous ne pouvez pas supprimer les groupes de sécurité associés à des instances, à d'autres groupes de sécurité ou à des interfaces réseau, s'affiche. listera les groupes de sécurité que vous pouvez supprimer; c'est-à-dire les groupes de sécurité non utilisés :)

35
NLail

Il s'agit de l'exemple de code écrit en boto (Python SDK for AWS) permettant de répertorier le groupe de sécurité en fonction du nombre d'instances auquel il est associé.

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

Boto Code

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

Sortie

Security-Group-1 0
Security-Group-2 1
Security-Group-3 0
Security-Group-4 3
21
Naveen Vijay

Après environ un an d'utilisation non vérifié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.

Il s'agissait d'une tâche ardue à effectuer via l'interface graphique Web. Je me suis donc tourné vers l'AWS CLI pour la rendre plus facile. J'ai trouvé un début sur comment faire ceci chez StackOverflow, mais c'était loin d'être complet. J'ai donc décidé d'écrire mon propre script. 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'ID de groupe, le nom de 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 Instances EC2 Elastic Load BalancersAWS Instances RDSAWS 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 rapporte le nombre de groupes restants dans le tableau une fois l'exclusion terminée.

  1. Enfin, j'affiche l'ID de groupe, le nom de groupe et la description des groupes qui restent. Ce sont les groupes «non utilisés» qui doivent être vérifiés et/ou supprimés. J'ai constaté que les SG entre les instances et Elastic Load Balancers (ELB) se référaient souvent les uns aux autres. Il est recommandé de procéder à des enquêtes manuelles afin de s’assurer qu’elles ne sont vraiment pas utilisées avant de supprimer les références croisées et les groupes de sécurité. Mais mon scénario au moins réduit cela à quelque chose de plus gérable.

NOTES: 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 vers celui-ci. 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 si vous avez des commentaires, des corrections 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
2
user2962402

Utilisation du SDK AWS node.js Je peux confirmer qu'AWS ne vous permet pas de 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 les erreurs avec élégance. Cela fonctionne pour le VPC classique et moderne. 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 }
2
Michael Connor

Parmi d'autres fonctions, ScoutSuite et Prowler signalent les groupes de sécurité EC2 inutilisés. Les deux sont open source.

1
Nan Zhong

Un exemple de boto imprimant les ID de groupe et les noms uniquement des groupes de sécurité ne possédant aucune instance en cours.

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

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 test if len(sg.instances()) == 0 et imprimer la valeur len(sg.instances()).

Par exemple.

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

Aux SG attaché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
1
Trane9991

C'est 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 utilisez uniquement des adresses IP, cette solution fonctionnera après la création d'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))
0
PythonNoob

Malheureusement, la réponse choisie n’est pas aussi précise que j’ai besoin (j’ai essayé de comprendre pourquoi mais j’ai préféré l’appliquer).
Si je vérifie TOUT NetworkInterfaces en cherchant des pièces jointes à une SecurityGroup, cela me donne des résultats partiels. Si je ne vérifie que EC2Instances, il me restitue également des résultats partiels. 

Voilà donc mon approche du problème: 

  1. Je reçois TOUS les EC2 SecurityGroups -> all_secgrp 
  2. Je reçois TOUTES les instances EC2 -> all_instances 
  3. Tous les SecurityGroups sont attachés à chaque instance
    1. Je supprime de all_secgrp chacun de ces groupes de sécurité (car attaché) 
  4. Pour chaque groupe de sécurité, je vérifie une association avec toutes les interfaces réseau (à l'aide de la fonction filter et du filtrage à l'aide de ce security-group-id).
    1. SI aucune association n'est trouvée, je supprime le groupe de sécurité de all_secgrp 

Ci-joint, vous pouvez voir un extrait de code. Ne vous plaignez pas pour 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
Echoes_86