Accès sécurisé aux instances EC2 sans ouvrir le port 22 : SSH via AWS Systems Manager

par Jérôme Dauge, Co-Founder

Si vous avez déjà travaillé avec des instances EC2 AWS, vous avez probablement utilisé SSH pour vous y connecter. C'est la méthode traditionnelle—simple et familière. Mais soyons honnêtes : l'accès SSH classique présente de sérieux inconvénients qui peuvent compromettre votre posture de sécurité et complexifier vos opérations.

Le problème avec SSH seul

Lorsque vous vous connectez directement en SSH à une instance EC2, vous vous exposez à plusieurs défis :

Risques de sécurité : Vous devez maintenir le port 22 ouvert sur Internet (ou au moins vers vos plages d'adresses IP), créant ainsi une surface d'attaque potentielle. Le port TCP 22 est une cible bien connue des attaques automatisées et des acteurs malveillants.

Casse-tête de gestion des clés : Les clés privées SSH doivent être distribuées aux membres de l'équipe. Comment les partager en toute sécurité ? Que se passe-t-il lorsqu'un membre quitte l'entreprise ? Vous devez faire une rotation des clés, les redistribuer, et espérer que personne ne les a stockées de manière non sécurisée.

Visibilité limitée : Qui est réellement connecté à vos instances en ce moment ? SSH ne fournit pas les capacités de surveillance nécessaires pour la conformité et la sécurité.

Contraintes réseau : Vos instances EC2 doivent être dans des sous-réseaux publics avec des IP publiques attachées, ce qui contredit les meilleures pratiques de sécurité consistant à garder les ressources dans des réseaux privés autant que possible.

Ce ne sont pas que des préoccupations théoriques—ce sont de véritables défis auxquels les équipes de développement et d'exploitation sont confrontées chaque jour.

AWS Systems Manager à la rescousse

Heureusement, AWS propose une meilleure solution : AWS Systems Manager (SSM). Si votre instance EC2 dispose de la politique AmazonSSMManagedInstanceCore attachée à son profil d'instance, vous êtes déjà prêt à utiliser SSM ! Pas de port 22, pas d'IP publiques, pas de clés partagées—juste un accès sécurisé et audité à vos instances.

SSM Session Manager crée des connexions sécurisées vers vos instances via l'API AWS, ce qui signifie :

  • Aucun besoin d'ouvrir des ports entrants
  • Aucun besoin de gérer des clés SSH
  • Fonctionne parfaitement avec les instances dans des sous-réseaux privés
  • Journalisation complète de qui se connecte et de ce qu'ils font
  • Contrôle d'accès centralisé via les politiques IAM

Mise en place de l'infrastructure

Voyons comment configurer un environnement sécurisé où SSM est le seul moyen d'accéder à vos instances EC2. Voici une stack CDK qui crée tout ce dont vous avez besoin :

  • Un VPC avec un sous-réseau isolé (aucun accès Internet)
  • Une instance EC2 dans ce sous-réseau isolé
  • Des VPC endpoints pour SSM, SSM Messages, EC2 et EC2 Messages
  • Des security groups et NACLs correctement configurés
export class BastionSsmStack extends Stack {
    constructor(scope: Construct, id: string, props?: StackProps) {
        super(scope, id, props);
 
        const vpc = new Vpc(this, "Vpc", {
            maxAzs: 1,
            natGateways: 0,
            subnetConfiguration: [
                {
                    name: "Private",
                    subnetType: aws_ec2.SubnetType.PRIVATE_ISOLATED,
                    cidrMask: 24,
                },
            ],
        });
 
        const privateNacl = new NetworkAcl(this, "PrivateNacl", {
            vpc: vpc,
            subnetSelection: {
                subnetType: aws_ec2.SubnetType.PRIVATE_ISOLATED,
            },
        });
 
        const instance = new Instance(this, "Instance", {
            instanceName: "ssm-session",
            instanceType: aws_ec2.InstanceType.of(
                aws_ec2.InstanceClass.T3,
                aws_ec2.InstanceSize.MICRO,
            ),
            machineImage: aws_ec2.MachineImage.latestAmazonLinux2023({}),
            requireImdsv2: true,
            userDataCausesReplacement: false,
            vpc: vpc,
            vpcSubnets: {
                subnetType: aws_ec2.SubnetType.PRIVATE_ISOLATED,
            },
        });
 
        instance.role.addManagedPolicy(
            aws_iam.ManagedPolicy.fromAwsManagedPolicyName(
                ManagedPolicies.AMAZON_SSM_MANAGED_INSTANCE_CORE,
            ),
        );
 
        // Configuration des NACLs pour les VPC endpoints
        privateNacl.addEntry("VpcEndpoints", {
            direction: aws_ec2.TrafficDirection.INGRESS,
            cidr: aws_ec2.AclCidr.anyIpv4(),
            traffic: aws_ec2.AclTraffic.tcpPort(443),
            ruleAction: aws_ec2.Action.ALLOW,
            ruleNumber: 100,
        });
 
        privateNacl.addEntry("S3Egress", {
            direction: aws_ec2.TrafficDirection.EGRESS,
            cidr: aws_ec2.AclCidr.anyIpv4(),
            traffic: aws_ec2.AclTraffic.tcpPort(443),
            ruleAction: aws_ec2.Action.ALLOW,
            ruleNumber: 100,
        });
 
        privateNacl.addEntry("Ephemeral", {
            direction: aws_ec2.TrafficDirection.INGRESS,
            cidr: aws_ec2.AclCidr.anyIpv4(),
            traffic: aws_ec2.AclTraffic.tcpPortRange(1024, 65535),
            ruleAction: aws_ec2.Action.ALLOW,
            ruleNumber: 101,
        });
 
        privateNacl.addEntry("EphemeralEgress", {
            direction: aws_ec2.TrafficDirection.EGRESS,
            cidr: aws_ec2.AclCidr.anyIpv4(),
            traffic: aws_ec2.AclTraffic.tcpPortRange(1024, 65535),
            ruleAction: aws_ec2.Action.ALLOW,
            ruleNumber: 101,
        });
 
        // Création des VPC endpoints
        const vpcEndpoints = [
            aws_ec2.InterfaceVpcEndpointAwsService.EC2,
            aws_ec2.InterfaceVpcEndpointAwsService.EC2_MESSAGES,
            aws_ec2.InterfaceVpcEndpointAwsService.SSM,
            aws_ec2.InterfaceVpcEndpointAwsService.SSM_MESSAGES,
        ];
 
        const vpcEndpointSecurityGroup = new aws_ec2.SecurityGroup(
            this,
            "VpcEndpointSecurityGroup",
            { vpc: vpc },
        );
 
        vpcEndpoints.map((value, index) =>
            vpc.addInterfaceEndpoint(`Endpoint${index}`, {
                service: value,
                subnets: {
                    subnetType: aws_ec2.SubnetType.PRIVATE_ISOLATED,
                },
                securityGroups: [vpcEndpointSecurityGroup],
            }),
        );
 
        instance.addSecurityGroup(vpcEndpointSecurityGroup);
    }
}

Note importante : La configuration aws_ec2.MachineImage.latestAmazonLinux2023({}) remplacera votre instance à chaque déploiement. Cela convient pour les tests mais doit être évité dans les environnements de production.

Se connecter avec SSM

Une fois votre infrastructure déployée, la connexion est simple.

Via la console AWS

La méthode la plus simple passe par la console AWS :

  1. Naviguez vers votre instance EC2
  2. Faites un clic droit et sélectionnez "Connect"
  3. Choisissez "Session Manager"
  4. Cliquez sur "Connect"

C'est tout ! Vous êtes maintenant connecté à votre instance sans jamais avoir touché au port 22.

Via l'AWS CLI

Pour ceux qui préfèrent la ligne de commande (et qui ne préfère pas ?), vous pouvez démarrer une session avec :

aws ssm start-session --target i-0d6bbfb0294c19598

Remplacez bien sûr i-0d6bbfb0294c19598 par votre ID d'instance réel.

Aller plus loin : SSH via SSM

Bien que les sessions SSM soient excellentes, vous avez parfois besoin de toute la puissance de SSH—peut-être pour utiliser scp pour des transferts de fichiers, ou pour configurer du port forwarding. La bonne nouvelle ? Vous pouvez utiliser SSH via SSM, combinant ainsi le meilleur des deux mondes.

Prérequis

Tout d'abord, installez le plugin Session Manager pour l'AWS CLI.

Ensuite, mettez à jour votre configuration SSH dans ~/.ssh/config :

# SSH over Session Manager
host i-* mi-*
    ProxyCommand sh -c "aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters 'portNumber=%p'"

Cette configuration indique à SSH d'utiliser SSM comme proxy chaque fois que vous vous connectez à une cible qui ressemble à un ID d'instance.

Se connecter avec SSH

Pour vous connecter via SSH, vous devrez temporairement associer votre clé publique SSH aux métadonnées de l'instance. Cela se fait en utilisant le service EC2 Instance Connect :

aws ec2-instance-connect send-ssh-public-key \
    --instance-id i-0d6bbfb0294c19598 \
    --instance-os-user ec2-user \
    --ssh-public-key file://~/.ssh/ssh_lab.pub

Important : Cette association ne dure qu'environ 60 secondes ! Vous devez initier votre connexion SSH dans cette fenêtre de temps. Cependant, une fois connecté, vous pouvez garder la session ouverte aussi longtemps que nécessaire.

Vous pouvez maintenant vous connecter en utilisant SSH :

ssh -i ~/.ssh/ssh_lab ec2-user@i-0d6bbfb0294c19598

Notez que vous utilisez l'ID d'instance comme nom d'hôte—pas besoin de connaître l'adresse IP ou de configurer un DNS.

Astuce : Si vous voulez utiliser le même utilisateur pour les sessions SSM et SSH, utilisez ssm-user au lieu de ec2-user. Par défaut, SSM vous connecte en tant que ssm-user, tandis que SSH utilise généralement ec2-user.

Transferts de fichiers

Le même mécanisme fonctionne parfaitement avec scp :

scp -i ~/.ssh/ssh_lab local-file.txt ec2-user@i-0d6bbfb0294c19598:/home/ec2-user/

Port Forwarding : accéder aux ressources privées

L'une des fonctionnalités les plus puissantes de SSM est le port forwarding. Imaginez que vous avez une base de données RDS dans un sous-réseau privé sans accès direct depuis votre poste de travail. Avec SSM, vous pouvez facilement créer un tunnel sécurisé.

aws ssm start-session \
    --target i-0d6bbfb0294c19598 \
    --document-name AWS-StartPortForwardingSessionToRemoteHost \
    --parameters '{"portNumber":["3306"],"localPortNumber":["3306"],"host":["your-rds-endpoint.region.rds.amazonaws.com"]}'

C'est tout ! Vous pouvez maintenant vous connecter à localhost:3306 avec votre client de base de données préféré, et votre trafic sera transmis en toute sécurité via SSM vers votre instance RDS.

Cela fonctionne pour n'importe quel service TCP : bases de données, Redis, APIs internes—tout ce dont vous avez besoin d'accéder dans vos sous-réseaux privés.

Bonus : SSM avec ECS

SSM n'est pas uniquement pour EC2—il fonctionne également avec les conteneurs ECS ! C'est incroyablement utile lorsque vous devez déboguer ou accéder à un conteneur en cours d'exécution dans votre cluster ECS.

Configuration d'ECS Exec

Pour activer l'accès SSM à vos conteneurs ECS, vous devez :

  1. Activer ECS Exec sur votre service
  2. Vous assurer que vos conteneurs ont un TTY attaché

Voici comment le faire avec CDK :

export class BastionEcsStack extends Stack {
    constructor(scope: Construct, id: string, props?: StackProps) {
        super(scope, id, props);
 
        const vpc = aws_ec2.Vpc.fromLookup(this, "Vpc", {
            vpcName: VPC_NAME,
        });
 
        const registry = new aws_ecr.Repository(this, "Repository", {
            repositoryName: "bastion",
        });
 
        const cluster = new aws_ecs.Cluster(this, "Cluster", {
            enableFargateCapacityProviders: true,
            vpc: vpc,
        });
 
        const bastionTask = new aws_ecs.TaskDefinition(this, "BastionTask", {
            compatibility: Compatibility.FARGATE,
            cpu: "256",
            memoryMiB: "512",
            runtimePlatform: {
                cpuArchitecture: aws_ecs.CpuArchitecture.ARM64,
                operatingSystemFamily: aws_ecs.OperatingSystemFamily.LINUX,
            }
        });
 
        const bastion = bastionTask.addContainer("Bastion", {
            image: aws_ecs.ContainerImage.fromEcrRepository(registry, "amazonlinux"),
            pseudoTerminal: true, // Ceci active le TTY
        });
 
        const bastionService = new aws_ecs.FargateService(this, "BastionService", {
            cluster: cluster,
            taskDefinition: bastionTask,
            enableExecuteCommand: true, // Ceci active ECS Exec
            vpcSubnets: {
                subnetType: SubnetType.PRIVATE_ISOLATED,
            },
        });
    }
}

Se connecter aux conteneurs ECS

La cible SSM pour les conteneurs ECS est construite à partir de trois composants (séparés par des underscores) :

ecs:CLUSTER-NAME_TASK-ID_CONTAINER-RUNTIME-ID

Par exemple :

aws ssm start-session \
    --target ecs:MonCluster_e0f752e4db444b4aa508fc496afde8e2_e0f752e4db444b4aa508fc496afde8e2-806556010

Le port forwarding fonctionne également avec les conteneurs ECS :

aws ssm start-session \
    --target ecs:MonCluster_e0f752e4db444b4aa508fc496afde8e2_e0f752e4db444b4aa508fc496afde8e2-806556010 \
    --document-name AWS-StartPortForwardingSessionToRemoteHost \
    --parameters '{"portNumber":["3306"],"localPortNumber":["3306"],"host":["your-rds-endpoint.region.rds.amazonaws.com"]}'

Astuce : Ces commandes peuvent devenir assez longues. Jetez un œil à e1s, un fantastique outil CLI qui rend le travail avec ECS beaucoup plus facile. Sélectionnez simplement votre conteneur et appuyez sur Shift+F pour démarrer le port forwarding !

Démonstration de port forwarding avec e1s

Pourquoi c'est important

Depuis que nous avons implémenté l'accès basé sur SSM chez Necko Technologies, nous avons constaté des améliorations significatives dans notre posture de sécurité et notre efficacité opérationnelle :

Sécurité renforcée : Plus de ports SSH ouverts sur Internet. Plus d'inquiétudes concernant les fuites de clés privées. L'accès est entièrement contrôlé via les politiques IAM, nous donnant un contrôle précis sur qui peut accéder à quelles instances.

Meilleure piste d'audit : Chaque session SSM est enregistrée dans CloudTrail. Nous pouvons voir exactement qui a accédé à quelle instance, quand, et ce qu'ils ont fait. C'est inestimable pour la conformité et les enquêtes de sécurité.

Opérations simplifiées : Plus de gestion de paires de clés SSH entre les membres de l'équipe. Lorsqu'une personne rejoint l'équipe, nous lui accordons des permissions IAM. Lorsqu'elle part, nous révoquons ces permissions. Simple.

Accès flexible : Que nous ayons besoin d'une session terminal rapide, d'une connexion SSH complète, ou d'un port forwarding sécurisé vers une base de données, SSM gère tout de manière transparente.

Fonctionne partout : Sous-réseaux privés, sous-réseaux isolés, conteneurs ECS—SSM fonctionne de manière cohérente dans toute notre infrastructure AWS.

Démarrer

Prêt à implémenter l'accès SSM dans votre propre infrastructure ? Voici le chemin le plus rapide :

  1. Assurez-vous que les instances ont l'agent SSM (il est préinstallé sur Amazon Linux 2023, Amazon Linux 2, Ubuntu et d'autres distributions majeures)

  2. Attachez la politique SSM au profil de votre instance :

    AmazonSSMManagedInstanceCore
    
  3. Testez la connexion en utilisant la console AWS ou le CLI

  4. Configurez SSH via SSM si vous avez besoin de fonctionnalités avancées comme les transferts de fichiers

Conclusion

L'accès SSH seul peut paraître pratique, mais ce n'est pas la solution la plus sûre ou la plus gérable pour l'infrastructure cloud moderne. AWS Systems Manager offre une meilleure approche, qui élimine le besoin de ports ouverts, simplifie la gestion des accès, fournit des journaux d'audit complets, et fonctionne de manière transparente avec les réseaux privés.

Chez Necko Technologies, l'adoption de SSM a rendu notre infrastructure plus sûre et nos opérations plus fluides. Nous ne nous inquiétons plus de la gestion des clés SSH, nous avons une visibilité complète sur qui accède à nos instances, et notre équipe de sécurité dort mieux la nuit en sachant que le port 22 est fermé.

Si vous utilisez encore SSH de manière traditionnelle pour accéder à vos instances EC2, essayez SSM. Votre équipe de sécurité (et votre futur vous-même) vous diront merci !

More articles

Guide de la conformité automatisée sur AWS

Apprenez à implémenter un framework de gouvernance qui détecte immédiatement les problèmes de sécurité tout en préparant automatiquement les preuves d'audit.

Lire l'étude de cas

Bonnes Pratiques Git et GitHub chez Necko Technologies

Découvrez l'approche de Necko Technologies concernant Git et GitHub, incluant les Commits Conventionnels, le squash merging et les releases automatisées avec release-please, pour maintenir un historique de code propre, améliorer la collaboration d'équipe et optimiser le flux de développement.

Lire l'étude de cas

Démarrez votre projet AWS