Introduction
Lorsqu’on expose publiquement des ressources AWS (comme une instance EC2), les groupes de sécurité permettent d’en contrôler le trafic entrant et sortant.
C’est en particulier nécessaire pour contrôler les connexions SSH entrantes si on expose une instance EC2 (donc une VM) sur laquelle peut par exemple tourner un site web.
En effet, même si l’accès à l’instance est déjà contrôlé par les key pairs, cela reste une très mauvaise pratique que d’exposer trop largement voire au web tout entier un accès ssh à celle-ci.
L’actualité récente a offert une piqûre de rappel à ce sujet avec la faille backdoor de la bibliothèque
xz-utils
qui a bien montré qu’on ne pouvait uniquement se reposer sur ssh pour sécuriser l’accès à une ressource.
La solution logique si on travaille seul sur cette ressource AWS est de n’y autoriser l’accès SSH que pour l’adresse IP publique de son PC. Cependant, cette IP peut être amenée à constamment changer (redémarrage du routeur, utilisation du PC en différents endroits).
C’est pourquoi nous allons voir dans cet article comment mettre à jour dynamiquement cette adresse IP à l’aide d’un script Powershell que l’on pourra ordonnancer sur son PC Windows.
Cas d’utilisation
Nous allons étudier le cas suivant :
- Instance AWS EC2 Linux disposant d’une IP publique (avec Elastic IP)
- Exposer les ports 80 et 443 (http et https) de l’instance à tout le web
- Exposer le port ssh 22 uniquement au PC de l’utilisateur
- Modifier dynamiquement l’IP définie dans la configuration de l’instance AWS EC2 pour l’accès ssh lorsque l’utilisateur ouvra sa session Windows
Nous allons donc voir comment utiliser les outils AWS pour Powershell qui permettent d’effectuer des tâches d’administration sur des ressources AWS, dont les instances EC2.
Dans notre cas, nous devrons travailler avec les groupes de sécurité qui permettent de contrôler les flux entrants et sortants vers notre instance EC2.
Il faudra donc créer un script Powershell qui modifiera la règle de sécurité du port ssh 22 pour le groupe de sécurité auquel est rattaché notre instance EC2, et le planifier pour qu’il s’exécute lorsque l’utilisateur ouvre sa session Windows.
Nous ne reviendrons pas ici sur la création de l’instance EC2 ni sur celle du groupe de sécurité. Nous considérons donc ce dernier créé avec des règles entrantes comme ci-dessous.
Création de l’utilisateur IAM
Le script aura besoin d’un accès au compte AWS utilisé pour gérer l’instance EC2, avec les droits suffisants pour effectuer des modifications sur les groupes de sécurité. Ceci est assuré par l’interface IAM (Identity and Acess Management) de ce compte.
Nous n’utiliserons pas ici la fonctionnalité IAM Identity Center qui s’adresse à des utilisateurs humains (activation par adresse e-mail, …).
Création du groupe d’utilisateurs
Conformément aux bonnes pratiques d’AWS, nous allons créer un groupe d’utilisateurs auquel nous allons accorder les droits suffisants plutôt que de les définir directement pour l’utilisateur.
Ceci s’effectue très simplement sur l’interface IAM du compte AWS à partir du menu Access management -> User groups
disponible sur le panneau gauche de la page.
Nous n’allons qu’indiquer le nom du groupe lors de sa création dans le menu suivant.
Et voilà, le groupe créé apparaît dans la liste de la page Access management -> User groups
.
Ajout d’une politique de permissions au groupe
Maintenant, nous allons accorder les droits suffisants au groupe créé afin de pouvoir effectuer les modifications de groupe de sécurité souhaitées.
Pour ce faire, il faut sélectionner le groupe dans la liste de la page Access management -> User groups
, choisir l’onglet Permissions
et cliquer sur Create inline policy
dans le menu déroulant Add permissions
.
Nous allons ajouter la politique de permissions suivante sous format json.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:DescribeInstanceAttribute",
"ec2:DescribeInstanceStatus",
"ec2:DescribeInstances",
"ec2:DescribeNetworkAcls",
"ec2:DescribeSecurityGroups",
"ec2:DescribeSecurityGroupRules",
"ec2:ModifySecurityGroupRules",
"ec2:CreateSecurityGroup",
"ec2:DeleteSecurityGroup"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ec2:RevokeSecurityGroupEgress",
"ec2:RevokeSecurityGroupIngress",
"ec2:AuthorizeSecurityGroupEgress",
"ec2:AuthorizeSecurityGroupIngress",
"ec2:UpdateSecurityGroupRuleDescriptionsEgress",
"ec2:UpdateSecurityGroupRuleDescriptionsIngress"
],
"Resource": "arn:aws:ec2:*:*:security-group/*"
}
]
}
Nous utiliserons donc en particulier les droits ec2:RevokeSecurityGroupIngress
et ec2:AuthorizeSecurityGroupIngress
pour effectuer les modifications que l’on souhaite dans les groupes de sécurité de l’instance EC2 (disponibles dans la ressource arn:aws:ec2:*:*:security-group/*
).
Enfin, la politique de permissions apparaît dans la définition du groupe d’utilisateurs.
Ajout d’un utilisateur IAM dans le compte AWS
Nous pouvons enfin créer un utilisateur IAM qui disposera des droits souhaités.
Il suffit de se rendre dans le menu Access management -> Users
disponible sur le panneau gauche de l’interface IAM, puis de créer l’utilisateur à partir de cette page.
Nous pourrons ensuite définir dans un premier temps son nom.
Et nous le rattacherons ensuite au groupe que nous avons créé pour qu’il bénéficie des droits voulus.
Enfin, après avoir validé la dernière étape de vérification, nous allons pouvoir sélectionner l’utilisateur et lui créer une clef d’accès dans l’onglet Security credentials
.
Il faut dans un premier temps définir le cas d’utilisation comme étant Command Line Interface (CLI)
et cocher la case de confirmation en bas.
Enfin, nous obtenons dans l’étape 3 la clef d’accès et son mot de passe à enregistrer dans un endroit sécurisé pour être utilisée plus tard par le script.
Ajouter la clef d’accès dans Windows Credential Manager
Nous allons stocker de façon sûre la clef d’accès générée dans Windows Credential Manager qui permettra aussi de la retrouver facilement dans le script Powershell.
Ceci s’effectue très simplement dans Powershell après avoir installé le module Powershell Credential Manager. Nous allons donner le nom de aws-editor-key
à l’entrée du Windows Credential Manager correspondant à cette clef.
Install-Module CredentialManager
# Récupérer la clef précédente et son mot de pass
$awsAccessKeyName = "XXXXXXXXXXXXX"
$awsAccessKeyPass = "XXXXXXXXXXXXX"
New-StoredCredential -Target 'aws-editor-key' -Type Generic -UserName $awsAccessKeyName -Password $awsAccessKeyPass -Persist 'LocalMachine'
Automatisation du changement de l’IP
Nous allons maintenant voir comment procéder au renouvellement automatique de l’adresse IP définie dans la règle entrante ssh de l’instance EC2.
Script Powershell
Après avoir créé un utilisateur IAM disposant des droits suffisants pour modifier les groupes de sécurité de notre instance, nous pouvons donc produire un script Powershell qui permettra d’automatiser le changement de l’adresse IP autorisée à se connecter en ssh à l’instance EC2.
# Dependencies : install the following PS modules
# Install-Module -Name AWS.Tools.Installer
# Install-AWSToolsModule AWS.Tools.EC2
# Install-Module -Name TUN.CredentialManager
# Prerequisites :
# - aws user credentials saved into Windows Credential Manager
# - on aws : usern rattached to policy with enough rights
#############
# Variables #
#############
# AWS configuration
# Regions can be an array (like @("eu-west-3", "eu-west-1")) to match the security groups
# or a string to apply the value to all security groups
$regions = "eu-west-3"
# Security group names array
$securityGroupNames = @("launch-wizard-3")
# Aws profile name where credentials are stored
$awsProfileName = "editor"
# In case of other value for ssh port; otherwise, default value is 22
# $sshPorts = @(22, 22)
# Same for cidrMask with default value 32
# $cidrMasks = @("32", "32")
# Windows Credential Manager target
$wcmAwsTarget = "aws-editor-key"
##########
# Script #
##########
# Get current public IP address
$ipAddress = (Invoke-WebRequest ifconfig.me/ip).Content.Trim()
# Set AWS credentials from profile, create profile if not done
$credentials = (Get-AWSCredential -ProfileName $awsProfileName)
if (!$credentials) {
# AWS credentials from Windows Credential Manager
$awsCredentials = Get-StoredCredential -Target $wcmAwsTarget
$accessKeyId = $awsCredentials.UserName
$secretAccessKey = (New-Object PSCredential 0, $awsCredentials.Password).GetNetworkCredential().Password
Set-AWSCredential -StoreAs $awsProfileName -AccessKey $accessKeyId -SecretKey $secretAccessKey
Write-Output "INFO - AWS credential profile '$awsProfileName' created"
}
Set-AWSCredential -ProfileName $awsProfileName
# Set default region for all following commands if $regions is a string
if ($regions.GetType().Name -eq "String") {
Set-DefaultAWSRegion -Region $regions
Write-Output "INFO - Region set to $regions for all security groups"
}
$i = 0
foreach ($securityGroupName in $securityGroupNames) {
# Setting the region if $regions is an array
if ($regions.GetType().Name -eq "Object[]") {
Set-DefaultAWSRegion -Region $regions[$i]
Write-Output "INFO - Region set to $regions[$i] for '$securityGroupName' security group"
}
$securityGroup = (Get-EC2SecurityGroup -GroupName $securityGroupName)
if (!$?) {
Write-Output "ERROR - '$securityGroupName' security group is not available on the AWS instance"
continue
}
$securityGroupId = $securityGroup.GroupId
# Setting ssh port and IP range CIDR mask
if (Test-Path variable:global:sshPorts) {
$sshPort = $sshPorts[$i]
} else {
$sshPort = 22
}
if (Test-Path variable:global:cidrMasks) {
$cidrMask = $cidrMask[$i]
} else {
$cidrMask = 32
}
$newIpRange = "$ipAddress/$cidrMask"
Write-Output "INFO - Setting $newIpRange IP range for ssh, '$securityGroupName' security group"
# Get security group IP range set for ssh inbound rule
$securityGroupIpRange = ($securityGroup.IpPermissions | Where-Object { $_.FromPort -eq $sshPort } | Select-Object -ExpandProperty Ipv4Ranges).CidrIp
if ($newIpRange -eq $securityGroupIpRange) {
Write-Output "INFO - $newIpRange ssh IP range was already set. Nothing to do for '$securityGroupName' security group"
continue
}
# Revoke current security group ingress rule for SSH
$ipPermissionOld = New-Object Amazon.EC2.Model.IpPermission
$ipPermissionOld.IpProtocol = "tcp"
$ipPermissionOld.FromPort = $sshPort
$ipPermissionOld.ToPort = $sshPort
$ipPermissionOld.IpRanges.Add($securityGroupIpRange)
Revoke-EC2SecurityGroupIngress -GroupId $securityGroupId -IpPermission $ipPermissionOld
Write-Output "INFO - $securityGroupIpRange ssh IP range removed for '$securityGroupName' security group"
# Add the security group ingress rule to allow SSH from current IP address
$IpRange = New-Object -TypeName Amazon.EC2.Model.IpRange
$IpRange.CidrIp = $newIpRange
$IpRange.Description = "SSH from Home"
$ipPermission = new-object Amazon.EC2.Model.IpPermission
$ipPermission.IpProtocol = "tcp"
$ipPermission.FromPort = $sshPort
$ipPermission.ToPort = $sshPort
$ipPermission.Ipv4Ranges = $IpRange
Grant-EC2SecurityGroupIngress -GroupId $securityGroupId -IpPermissions $ipPermission
Write-Output "INFO - $newIpRange ssh IP range successfully set for '$securityGroupName' security group"
(++$i)
}
Quelques remarques sur le script :
Les dépendances en modules Powershell sont :
AWS.Tools.Installer
puisAWS.Tools.EC2
, etTUN.CredentialManager
pour l’accès à Windows Credential Manager.Le script prend en charge plusieurs groupes de sécurité si besoin dans le tableau
$securityGroupNames
L’utilisateur peut soit définir une région AWS unique dans la variable
$regions
pour tous les groupes de sécurité, soit les donner sous forme de tableau pour chacun des groupes définis dans$securityGroupNames
.Il est possible de définir pour chaque groupe de sécurité le port ssh ainsi que le masque CIDR de la plage d’IP autorisée (tableaux
$sshPorts
et$cidrMasks
), sinon les valeurs par défaut22
et32
seront respectivement appliquées. Pour les masques CIDR, cela n’est pas pertinent de choisir une autre valeur puisqu’on ne veut appliquer la règle qu’à une seule IP, mais cela laisse la porte ouverte à des modifications pour l’appliquer à une plage d’IP.On récupère l’IP publique du PC avec la commande
(Invoke-WebRequest ifconfig.me/ip).Content.Trim()
Les identifiants de l’utilisateur IAM sont récupérés de Windows Credential Manager puis appliqués à la configuration AWS en définissant un profil
$awsProfileName
avec la commandeSet-AWSCredential -ProfileName $awsProfileName
. De même, la région AWS est définie avec la commandeSet-DefaultAWSRegion -Region $region
Ensuite, dans la boucle sur les différents groupes de sécurité, on effectue les tâches suivantes :
- Comparaison de la nouvelle plage d’IP avec celle récupérée d’AWS
- Si identique, on ne fait rien.
- Si différente, on supprime la règle de sécurité ssh actuelle du groupe de sécurité, et on en crée une nouvelle à partir de l’adresse IP du PC.
Et voilà ! Lorsque le script est exécuté vous pouvez ensuite si les changements ont bien été appliqués aux groupes de sécurité de l’instance EC2.
Ordonnancement du script
Maintenant, nous allons ordonnancer le lancement du script grâce au planificateur de tâches Windows.
Il suffit de se rendre dans le menu Action -> Créer une tâche
tout d’abord.
Après avoir rempli les champs de l’onglet “Général” à sa convenance, on va définir les conditions de déclenchement du script dans l’onglet “Déclencheurs” en cliquant sur Nouveau
.
Nous allons définir les champs :
Lancer la tâche
avec la valeurAu moment de la connexion à une session utilisateur
Connexion depuis l'ordinateur local
en-dessous
De la sorte, nous nous assurons que le script est lancé à chaque fois que l’on se connecte à sa session utilisateur, ce qui était notre objectif initial.
On peut également définir la politique d’essais en cas d’échec et d’autres paramètres d’exécution dans l’onglet Paramètres
.
On peut alors vérifier si le script s’est bien exécuté en regardant le tableau des tâches planifiées.