Cómo encontrar grupos de seguridad de Amazon EC2 no utilizados


93

Estoy tratando de encontrar una manera de determinar los grupos de seguridad huérfanos para poder limpiarlos y deshacerme de ellos. ¿Alguien conoce alguna forma de descubrir grupos de seguridad no utilizados?

Ya sea a través de la consola o con las herramientas de línea de comando funcionará (Ejecutar herramientas de línea de comando en máquinas Linux y OSX).


4
My Kingdom para una respuesta que responda completamente a esta pregunta, sin excepciones para objetos de larga duración que no son de instancia (RDS, ELB, ALB) que pueden tener SG asignados, y no implica 'seleccionar todo, luego eliminar' fin de semana de miedo -Enfoque destructor. :)
Jesse Adelman

Respuestas:


78

Nota: esto solo considera el uso de seguridad en EC2, no otros servicios como RDS. Deberá trabajar más para incluir los grupos de seguridad que se utilizan fuera de EC2. Lo bueno es que no puede fácilmente (puede que ni siquiera sea posible) eliminar grupos de seguridad activos si pierde uno asociado con otro servicio.

Con la herramienta AWS CLI más reciente, encontré una manera fácil de obtener lo que necesito:

Primero, obtenga una lista de todos los grupos de seguridad

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

A continuación, obtener todos los grupos de seguridad atadas a una instancia, y luego entubada a sortcontinuación uniq:

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

Luego júntelo y compare las 2 listas y vea lo que no se está utilizando de la lista maestra:

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í, solo tengo una región y los scripts de AWS tienen su región de origen configurada a través de variables ambientales. Me interesaría ver una versión multirregional de este script.
Ray

1
es posible que desee agregar un --filtro para su vpc para que no tenga que ver otro vpc predeterminado sg
shadowbq

2
Un grupo de seguridad también puede estar en uso por un ELB. Este comando enumerará el conjunto uniq de ID de grupos de seguridad a los que hacen referencia los ELB en la región predeterminada:aws elb describe-load-balancers --query 'LoadBalancerDescriptions[*].SecurityGroups[*]' --output text | tr '\t' '\n' | sort | uniq
astletron

2
Un grupo de seguridad EC2 también puede estar en uso por una instancia RDS. Este comando indica el ID de grupo de seguridad es utilizado por los casos de RDS en la región por defecto:aws rds describe-db-security-groups --query 'DBSecurityGroups[*].EC2SecurityGroups[*].EC2SecurityGroupId' --output text | tr '\t' '\n' | sort | uniq
aharden

2
También puede utilizar aws ec2 describe-network-interfaces --query 'NetworkInterfaces[*].Groups[*].GroupId' --output text| tr '\t' '\n' | sort | uniqpara describir simplemente las interfaces de red.
Jonathan

63

Si selecciona todos sus grupos de seguridad en la consola de EC2, luego presiona acciones -> Eliminar grupos de seguridad, aparecerá una ventana emergente que le indica que no puede eliminar los grupos de seguridad que están adjuntos a instancias, otros grupos de seguridad o interfaces de red, y enumerará los grupos de seguridad que puede eliminar; es decir, los grupos de seguridad no utilizados :)


15
Aunque debo estar de acuerdo, usar "seleccionar todo + eliminar" no suele ser un buen hábito.
Balmipour

3
Si no está seguro de si funcionará, puede crear un grupo de seguridad ficticio y adjuntarle algo, intentar eliminarlo y ver que no se lo permita.
NLail

2
No es necesario que confirmes realmente la eliminación, en la ventana emergente te mostrará un desglose de cuáles se pueden eliminar (huérfanos) y cuáles no. Luego puede presionar cancelar y luego eliminar los huérfanos.
rjarmstrong

4
Lo que no entiendo es esto: si la consola de AWS puede ofrecer esta información cuando hace esta maniobra de miedo, ¿por qué no comparten cómo hacer lo mismo a través de la API? No es que esto no sea algo que probablemente se necesite en entornos de campo marrón ...
Jesse Adelman

1
sé valiente :: hazlo
zanuka

29

Este es el código de muestra escrito en boto (Python SDK para AWS) para enumerar el grupo de seguridad con la cantidad de instancias con las que está asociado.

Puede usar esta lógica para obtener lo mismo en la línea de comando también

Código Boto

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

Salida

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

¡Bonito y fácil! Gracias
Chris Koston

6
bueno, sí, pero ¿qué pasa con los elbos?
Ilja

También tenga en cuenta que esto incluye solo las instancias en ejecución. Tampoco puede eliminar un SG que esté vinculado a una instancia detenida.
AgDude

6
Esto ignora las interfaces de servicios como RDS. RDS es el propietario de la instancia, pero usted es el propietario de ENI. Creo que ElasticSearch y ELB funcionan de manera similar y no aparecerían con este script
rajat banerjee

6

Después de aproximadamente un año de uso sin auditar, me pareció necesario auditar mis grupos de seguridad de AWS EC2 y limpiar los grupos heredados no utilizados.

Esta fue una tarea desalentadora de realizar a través de la GUI web, por lo que busqué la AWS CLI para facilitar la tarea. Encontré un comienzo sobre cómo hacer esto en StackOverflow, pero estaba lejos de ser completo. Así que decidí escribir mi propio guión. Usé AWS CLI, MySQL y algo de "Bash-foo" para realizar lo siguiente:

  1. Obtenga una lista de todos los grupos de seguridad de EC2. Guardo la identificación del grupo, el nombre del grupo y la descripción en una tabla llamada "grupos" en una base de datos MySQL llamada aws_security_groups en el localhost. El número total de grupos encontrados se informa al usuario.

  2. Obtenga una lista de todos los grupos de seguridad asociados con cada uno de los siguientes servicios y exclúyalos de la tabla: EC2 Istances EC2 Elastic Load Balancers AWS RDS Instances AWS OpsWorks (no debe eliminarse por Amazon) Grupos de seguridad predeterminados (no se puede eliminar ) ElastiCache

Para cada servicio, informo un recuento del número de grupos que quedan en la tabla después de que se completa la exclusión.

  1. Finalmente, muestro la identificación del grupo, el nombre del grupo y la descripción de los grupos que quedan. Estos son los grupos "no utilizados" que necesitan ser auditados y / o eliminados. Descubrí que los SG entre instancias y los Elastic Load Balancers (ELB) a menudo se refieren entre sí. Es una buena práctica realizar una investigación manual para asegurarse de que realmente no estén en uso antes de eliminar las referencias cruzadas y eliminar los grupos de seguridad. Pero mi guión al menos reduce esto a algo más manejable.

NOTAS: 1. Querrá crear un archivo para almacenar su host MySQL, nombre de usuario y contraseña y apuntar la variable $ DBCONFIG hacia él. Debería estar estructurado así:

[mysql]
host=your-mysql-server-host.com
user=your-mysql-user
password=your-mysql-user-password
  1. Puede cambiar el nombre de la base de datos si lo desea; asegúrese de cambiar la variable $ DB en el script

Avíseme si lo encuentra útil o si tiene algún comentario, corrección o mejora.

Aquí está el guión.

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

Y aquí está el sql para crear la base de datos.

-- 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 ejemplo de boto que imprime los ID de grupo y los nombres solo de los grupos de seguridad que no tienen instancias actuales.

También muestra cómo especificar qué región le preocupa.

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

Para confirmar los grupos de seguridad están todavía siendo utilizados debe revertir o eliminar la if len(sg.instances()) == 0prueba e imprimir el len(sg.instances())valor de salida.

P.ej

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

3

Con el SDK de AWS de node.js, puedo confirmar que AWS no le permite eliminar los grupos de seguridad que están en uso. Escribí un script que simplemente intenta eliminar todos los grupos y maneja con gracia los errores. Esto funciona para VPC clásicas y modernas. El mensaje de error se puede ver a continuación.

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

A los SG conectados a las interfaces de red:

Por nombre:

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

Por id:

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

0

Hay una herramienta en el mercado de AWS que hace que esto sea mucho más fácil. Le muestra qué grupos están adjuntos / desconectados para una fácil eliminación, pero también compara sus registros de flujo de VPC con las reglas del grupo de seguridad y le muestra qué reglas SG están en uso o no. AWS publicó una solución ELK-stack para hacer esto, pero era ridículamente compleja.

Aquí está la herramienta y un descargo de responsabilidad de que trabajé en ella. Pero espero que todos lo encuentren pertinente: https://www.piasoftware.net/single-post/2018/04/24/VIDEO-Watch-as-we-clean-up-EC2-security-groups-in-just -unos minutos


0

Desafortunadamente, la respuesta elegida no es tan precisa como necesito (he intentado investigar el por qué, pero he preferido implementarlo).
Si reviso TODOS NetworkInterfaces, buscando adjuntos a alguno SecurityGroup, obtengo resultados parciales. Si solo verifico EC2Instances, también obtengo resultados parciales.

Así que ese es mi enfoque del problema:

  1. Obtengo TODOS los grupos de seguridad de EC2 -> all_secgrp
  2. Obtengo TODAS las instancias EC2 -> all_instances
  3. Para cada instancia, obtengo todos los grupos de seguridad adjuntos
    1. Elimino de all_secgrp cada uno de estos SecurityGroup (porque está adjunto)
  4. Para cada SecurityGroup, verifico una asociación con cualquier NetworkInterfaces (usando la filterfunción y filtrando usando eso security-group-id)
    1. SI no se encuentra ninguna asociación, elimino el grupo de seguridad de all_secgrp

Adjunto puede ver un fragmento de código. No te quejes por la eficiencia, pero trata de optimizarla si quieres.

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

Este es un problema difícil si tiene grupos de seguridad que hacen referencia a otros grupos de seguridad en las reglas. Si es así, tendrá que resolver DependencyErrors, que no es trivial.

Si solo usa direcciones IP, esta solución funcionará después de crear un cliente 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))

Esto no cubrirá los SG utilizados por RDS
alexandernst
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.