Seguridad Serverless, Higiene de Cuentas IAM

Seguridad Serverless, Higiene de Cuentas IAM

Seguridad Serverless con AWS Lambda ejecutada por AWS CloudWatch y notificaciones via SNS

¿Cómo hacer para mantener “la Higiene” de nuestos usuarios IAM? Es la pregunta que se me genera, sin esfuerzo operacional.

Un poco de contexto

AWS Access Keys son las credenciales utilizadas por el usuario de IAM o el Master de la cuenta de AWS. Son utilizadas para la parte programatica de AWS, en AWS CLI o AWS API (directamente o utilizando el SDK de AWS). Las claves de acceso constan de dos partes: un ID de clave de acceso y una clave de acceso secreta. Al igual que el nombre de usuario y la contraseña, tenemos que proporcionar ambos juntos para autentificar las solicitudes. Una recomendación de seguridad sugiere que sólo debemos utilizar los roles IAM en lugar de las claves de acceso, pero aún así en algunos casos necesitamos tener las claves de acceso. Estas claves de acceso proporcionará a un usuario el acceso completo a un conjunto de recursos definidos por la Política de Usuario IAM. Por lo tanto, si las claves de acceso se pierden o fueron a manos equivocadas, sus recursos estarian comprometidos.

Hay un conjunto de Best Practice IAM, definidas por AWS, para ayudar a asegurar sus recursos. Por ejemplo, relacionado con IAM tenemos:

Do Not Share Access Keys

Las claves de acceso no deben ser compartidas en público y deben ser guardadas en un lugar seguro.

Rotate Credentials Regularly

Debemos rotar las credenciales regularmente. Si una contraseña o clave de acceso se ve comprometida sin su conocimiento, debe rotarse o deshabilitarse. Tenemos en cuenta que podriamos tener servicios comprometidos.

Remove Unnecessary Credentials

Es importante eliminar las contraseñas y las claves de acceso no utilizadas. ¿ Para que tener cuentas, con permisos, sin utilizar? No tiene sentido y es un riesgo a mitigar.

Si tenemos muchas cuentas de AWS esta tarea puede ser tediosa. Por eso, como en muchas actividades de la Seguridad moderna, debemos automatizar.

Compliance

Si revisamos los CIS Controls AWS Foundations vemos los siguientes consejos:

Debemos deshabilitar las credenciales, no utilizadas, por mas de 90 dias y rotarlas, cada 90 dias.

Estrategia

Para cumplir con los siguientes requisitos de cumplimiento, vamos a implementaremos una función Lambda que se encargará de lo siguiente.

Auditoria

Comprobar el cumplimiento todos los viernes, via AWS CloudWatch, y enviar un reporte atraves de AWS SNS al equipo de SecOps.

Otras cosas que podriamos agregar serian, por ejemplo:

Saneamiento

Si no se utiliza la Clave de Acceso desde que los últimos 45 días están «Activos», deshabilite la Clave de Acceso y publique el mensaje al equipo de SecOps.

Rotacion

Si la creación de la Clave de Acceso ha pasado los 85 días, genere una nueva Clave de Acceso para el usuario y publique el mensaje al equipo de SecOps. El equipo de SecOps informará al usuario para que actualice todas sus aplicaciones con la nueva Clave de Acceso. Borrar la antigua Clave de Acceso, automáticamente, después de 90 días. Esta, en particular, habria que revisar el procedimiento.

Por el momento vamos a crear una funcion que audite, en Python.

# Lista IAM con Llaves de mas de 90 Dias.

import datetime, boto3, os, json
from botocore.exceptions import ClientError

# Variables Globales.
VariableGlobal  = {}
VariableGlobal['Owner']                 = "Santiago Fernandez"
VariableGlobal['Environment']           = "Test"
VariableGlobal['REGION_NAME']           = "us-east-1"
VariableGlobal['tagName']               = "SecOps"
VariableGlobal['key_age']               = "90"
VariableGlobal['SecOpsTopicArn']        = ""

def get_usr_old_keys( keyAge ):
    client = boto3.client('iam',region_name = VariableGlobal['REGION_NAME'])
    usersList=client.list_users()

    # Delta entre dia de la fecha y los dias que ingresamos, en la viriable 'key_age'.
    timeLimit=datetime.datetime.now() - datetime.timedelta( days = int(keyAge) )
    # Primer linea de mensaje.
    usrsWithOldKeys = {'Users':[],'Description':'Lista de Usuarios con llaves mas viejas que (>=) {} dias'.format(keyAge),'KeyAgeCutOff':keyAge}

    # Iterar a través de la lista de usuarios y comparar con la "edad de las llaves". Marcar los que tienen llaves antiguas.
    for k in usersList['Users']:
        accessKeys=client.list_access_keys(UserName=k['UserName'])

        # Iterar sobre los Usuarios.
        for key in accessKeys['AccessKeyMetadata']:
            if key['CreateDate'].date() <= timeLimit.date():
                usrsWithOldKeys['Users'].append({ 'UserName': k['UserName'], 'KeyAgeInDays': (datetime.date.today() - key['CreateDate'].date()).days })

        # Lineas del mensaje, con la totalidad. 
        if not usrsWithOldKeys['Users']:
            usrsWithOldKeys['OldKeyCount'] = 'Encontramos 0 claves que son más antiguas que {} dias'.format(keyAge)
        else:
            usrsWithOldKeys['OldKeyCount'] = 'Encontramos {0} que son mas viejas que {1} dias'.format(len(usrsWithOldKeys['Users']), keyAge)

    return usrsWithOldKeys

def sns_notificacion(usrsWithOldKeys):
    snsClient = boto3.client('sns',region_name = VariableGlobal['REGION_NAME'])
    try:
        # Enviamos el Mensaje, via SNS. Hacemos el Dump, del mensaje.
        snsClient.get_topic_attributes( TopicArn= VariableGlobal['SecOpsTopicArn'] )
        snsClient.publish(TopicArn = VariableGlobal['SecOpsTopicArn'], Message = json.dumps(usrsWithOldKeys, indent=4) )
        usrsWithOldKeys['SecOpsEmailed']="Si"
    except ClientError as e:
        # No se pudo enviar el Mensaje.
        usrsWithOldKeys['SecOpsEmailed']="No - SecOpsTopicArn es Incorrecto"


def lambda_handler(event, context):   
    # Enviamos las variables, preconfiguradas. Dias y SNS.
    VariableGlobal['key_age'] = int(os.getenv('key_age',90))
    VariableGlobal['SecOpsTopicArn']=str(os.getenv('SecOpsTopicArn'))

    users_old_key = get_usr_old_keys( VariableGlobal['key_age'] )
    sns_notificacion(users_old_key)

    return users_old_key

Hands On

Función Lambda

En la consola de Lambda de AWS usando el desplegable «Servicios» y busca «Lambda». Dentro de la consola Lambda, vaya a «Funciones» y presione «Crear función». Rellene la información básica necesaria para la función Lambda de AWS y siga adelante.

Se creo el Rol, automaticamente, pero podriamos reutilizar alguno con los permisos pertinentes. Depende de cada uno y como organiza su IAM. Vamos a modificarlo para poder pulir los permisos.

Buscamos el Rol que creamos y agregamos las politicas de permisos. En nuestro caso, por ahora, IAMReadOnlyAccess y AmazonSNSFullAccess. Tengan en cuenta si modifican la funcion Lambda para «Inhabilitar» deberian tener derechos de escritura sobre IAM.

SNS

Vamos a crear un Topico en AWS SNS.

Ahora agregamos los correos, del equipo de SecOps, generando las suscripciones. Podriamos haber ejecutado otra Lambda o generar un SNS Json para poder realizar diferentes acciones. ¡Tenemos muchas opciones para jugar!

En las variables de entorno, de la funcion Lambda, debemos especificar el ARN del tema del AWS SNS. Asi que copienla para agregarla.

¡Vamos a probar nuestra funcion!

¡Excelente! Ya nos esta reportando, lo que requeriamos. ¡Tenemos un usuario que tiene las mismas llaves hace 126 dias! El equipo de SecOps debe de tomar cartas en el asunto.

Revisamos el correo, donde deberiamos de tener la notificacion via SNS.

¡Vamos a automatizar el Trigger! Con AWS CloudWatch.

En mi caso, voy hacer que corra todos los Viernes. Pero podemos poner las condiciones que necesitemos en el Cron.

¡Espero que les sirva!

Todo esto es un rejunte de información de Internet. Espero lo utilicen y mejoren, a su gusto. Esto es un gran ejemplo de Seguridad Serverless. En próximas entradas vamos pulir la función y crear algunas Lambdas mas de Seguridad.

Cabe aclarar que estos servicios pueden solventarlos con Security Hub, sin necesidad de Codear. ¡Pero pierde la gracia!