Objetivo
Fazer o levantamento de usuários IAM sem MFA configurado e com chaves de acesso com mais de 180 dias de criadas.
Método
Este script faz o levantamento em todas as contas AWS dos usuários IAM que não tem MFA e das access keys com mais de 180 dias.
Para que o script execute corretamente o arquivo credentials só pode ter uma entrada de perfil para cada conta AWS. Se houver mais de uma access key/secret key no arquivo credentials, então, a saída do script ficará duplicada. Se não houver uma credencial associada a alguma conta AWS, a conta em questão não será analisada. Assim, se queremos analisar 20 contas AWS precisaremos de 20 credenciais cadastradas.
O formato de cada credencial no arquivo credentials deve ser semelhante a esta:
[aplicacoes]
aws_access_key_id = AKIADF4CHAVEFAKEOPYNDW2VF4
aws_secret_access_key = i7U!!9887chavefakehKUuyh9ajQVnC6BE4bgqW
Em máquinas Windows, por padrão, o arquivo credentials fica localizado em %USERPROFILE%\.aws\
O script também considera que as 3 primeiras linhas do arquivo credentials são destinadas para o perfil default. Se o perfil default não existe no arquivo adicione 3 linhas em branco no início do arquivo.
O script está todo comentado com a finalidade de ser o mais didático possível.
Código
import boto3
import os.path
from datetime import date
'''
Este script faz o levantamento em todas as contas dos usuários IAM que não tem MFA e das access keys com mais de
180 dias.
Para que o script execute corretamente o arquivo credencials só pode ter uma entrada de perfil para cada conta AWS.
Se houver mais de uma access key/secret key no arquivo credecials a saída do script ficará duplicada.
Se não houver uma credencial associada a alguma conta AWS, a conta em questão não será analisada.
Assim, se queremos analisar 20 contas AWS precisaremos de 20 credenciais cadastradas.
O formato de cada credencial no arquivo credencials deve ser semelhante a esta:
[aplicacoes]
aws_access_key_id = AKIADF4CHAVEFAKEOPYNDW2VF4
aws_secret_access_key = i7U!!9887chavefakehKUuyh9ajQVnC6BE4bgqW
Em máquinas Windows, por padrão, o arquivo credentials fica localizado em %USERPROFILE%\.aws\
O script também considera que as 3 primeiras linhas do arquivo credencials são destinadas para o perfil default.
'''
def cria_user_session(perfil):
session = boto3.Session(profile_name=perfil) # abre uma sessão
return session
def cria_sts_client_object(session):
client_sts = session.client('sts') # abre um cliente do tipo sts para recuperar o account_id.
return client_sts
def cria_iam_client_object(session):
client_iam = session.client('iam') # abre um cliente do tipo iam para criar os usuários.
return client_iam
def lista_aws_accounts():
path = "~/.aws/credentials" # path do arquivo credencials.
full_path = os.path.expanduser(path) # full_path armazena o path completo do arquivo credencials.
contasAWS = []
with open(full_path, 'r') as f: # abre o arquivo de credenciais.
next(f) # ignora a credencial "default" (3 primeiras linhas).
next(f)
next(f)
lines = f.readlines() # lê o arquivo e joga as linhas do arquivo como itens de lista lines.
f.close()
for line in lines: # faz um loop em lines para verificar se é um nome de profile ou access_key ou secret_access_key.
if line.startswith('['): # se a linha começar com colchetes então é o nome do perfil AWS.
perfil = line[1:-2]
contasAWS.append(perfil)
return contasAWS
def lista_usuarios_da_conta(client_iam):
iam_users = []
response_iam = client_iam.list_users(MaxItems=1000) # Limite máximo de usuários retornados por página 1000
for user in response_iam['Users']: # cria uma lista com todos os IAM users.
iam_users.append(user['UserName'])
while 'Marker' in response_iam: # Adiciona novos usuários à lista, caso ocorra paginação. Procura pela key \
# "Marker" no dicionário.
response_iam = client_iam.list_users(Marker=response_iam['Marker']) # Faz busca na nova página usando o \
# parâmetro "Marker" na chamada do método list_users.
for user in response_iam['Users']: # Adiciona os usuários da nova página na lista iam_users.
iam_users.append(user['UserName'])
return iam_users
def conta_usuarios_sem_mfa(iam_users, client_iam):
count_no_mfa = 0 # variável que armazena o número de usuários sem MFA.
for iam_user in iam_users: # cria uma lista dos usuários que não tem MFA e conta o número de usuários.
resp1 = client_iam.list_mfa_devices(UserName=iam_user) # recupera os dados de MFA de um usuário.
if not resp1['MFADevices']: # se não houver a key 'MFADevices', então, conta como usuário sem MFA.
count_no_mfa = count_no_mfa + 1
return count_no_mfa
def lista_usuarios_sem_mfa(iam_users, client_iam):
no_mfa_users = [] # lista de todos os usuários sem MFA
for iam_user in iam_users: # cria uma lista dos usuários que não tem MFA e conta o número de usuários.
resp1 = client_iam.list_mfa_devices(UserName=iam_user) # recupera os dados de MFA de um usuário.
if not resp1['MFADevices']: # se não houver a key 'MFADevices', então, conta como usuário sem MFA.
no_mfa_users.append(iam_user) # adiciona o usuário na lista de usuários sem MFA.
return no_mfa_users
def conta_chaves_expiradas(iam_users, client_iam):
count_out_key = 0 # variável que armazena o número de chaves de acesso sem rotação.
key_age = [] # lista da idade de criação das keys em dias
for iam_user in iam_users: # conta os usuários com a chave expirada.
resp2 = client_iam.list_access_keys(UserName=iam_user) # recupera os dados de chaves de um usuário.
if len(resp2['AccessKeyMetadata']) > 0: # verifica se exite uma AccessKey para o usuário e se houver \
# vai listar a idade da key.
accesskeydate = resp2['AccessKeyMetadata'][0]['CreateDate'].date() # recupera a idade da chave.
currentdate = date.today() # recupera a data corrente.
active_days = currentdate - accesskeydate # calcula a idade da chave em dias.
if active_days.days > 180: # imprime se a key for maior que 180 dias e conta o número de keys expiradas.
count_out_key = count_out_key + 1
return count_out_key
def lista_usuarios_chaves_expiradas(iam_users, client_iam):
key_age = [] # lista da idade de criação das keys em dias
users_out_key = {}
for iam_user in iam_users: # conta os usuários com a chave expirada.
resp2 = client_iam.list_access_keys(UserName=iam_user) # recupera os dados de chaves de um usuário.
if len(resp2['AccessKeyMetadata']) > 0: # verifica se exite uma AccessKey para o usuário e se houver \
# vai listar a idade da key.
accesskeydate = resp2['AccessKeyMetadata'][0]['CreateDate'].date() # recupera a idade da chave.
currentdate = date.today() # recupera a data corrente.
active_days = currentdate - accesskeydate # calcula a idade da chave em dias.
if active_days.days > 180: # imprime se a key for maior que 180 dias e conta o número de keys expiradas.
users_out_key[iam_user] = active_days.days
return users_out_key
def main():
contasAWS = lista_aws_accounts()
for perfil in contasAWS:
session = cria_user_session(perfil) # abre uma sessão.
client_sts = cria_sts_client_object(session) # abre um cliente do tipo sts para recuperar o account_id.
client_iam = cria_iam_client_object(session) # abre um cliente do tipo iam para buscar os usuários.
iam_users = [] # lista com todos os usuários IAM de uma conta AWS
iam_users = lista_usuarios_da_conta(client_iam)
no_mfa_users = lista_usuarios_sem_mfa(iam_users, client_iam)
count_no_mfa = conta_usuarios_sem_mfa(iam_users, client_iam)
count_out_key = conta_chaves_expiradas(iam_users, client_iam)
users_out_key = lista_usuarios_chaves_expiradas(iam_users, client_iam)
print('Account_ID :', client_sts.get_caller_identity().get('Account')) # imprime o ID da conta AWS.
print('→ Número de Usuários IAM sem MFA: ', count_no_mfa)
print('→ Usuários IAM sem MFA: ')
for user in no_mfa_users: # iteração para imprimir os usuários sem MFA.
print('\tUser: {}'.format(user))
print('→ Número de chaves expiradas: ', count_out_key)
print('→ Lista de usuários com access key expirada e idade da chave em dias:')
for user_out_key, days in users_out_key.items():
print('\tUser: {}\tDays: {}'.format(user_out_key, days))
print('++++++++++++++++++++++++++++++++++++') # printa separador.
if __name__=="__main__":
main()
It’s done!