AWS Lambda : comment exécuter des commandes sur une instance Linux EC2 en HTTPS

AWS Lambda : comment exécuter des commandes sur une instance Linux EC2 en HTTPS

Exécuter une commande sur une instance EC2 en passant par une fonction Lambda

Dans cet article nous allons mettre en place une fonction AWS Lambda pour exécuter des commandes sur une instance Linux EC2 via un appel API en HTTPS. Cette Lambda sera utile sur des environnements où les connexions SSH vers internet sont limitées ou complètement fermées.

C'est quoi AWS Lambda ?

AWS Lambda est un service de calcul à la demande (Cloud compute service) fourni par Amazon dans son catalogue de services AWS (Amazon Web Services), il a été annoncé pendant le re:Invent de novembre 2014. AWS Lambda offre la possibilité d'exécuter du code sans avoir à gérer des serveurs, c'est le concept Serverless. AWS Lambda repose sur une architecture orientée événements (Event-Driven).

C'est quoi une fonction Lambda ?

Une fonction Lambda c'est une ou plusieurs portions de code représentant un programme informatique managé par le service AWS Lambda. Les ressources nécessaires pour exécuter ce code sont gérées de manière transparente pour le développeur. Le service AWS Lambda supporte les langages de programmation ci-dessous (à la date de la rédaction de cet article) :

  1. Python

  2. Go

  3. Node.JS

  4. C#

  5. PowerShell Core

  6. Java

Pourquoi AWS Lambda ?

L'architecture Serverless et orientée événements (Event-Driven) de AWS Lambda présente plusieurs avantages, parmi ceux-ci :

  1. Se concentrer sur le code. Aucun serveur n'est à installer, sauvegarder, surveiller ou mettre à jour. AWS gère cette partie pour nous

  2. Bénéficier de la haute disponibilité de la plateforme AWS

Prérequis

Afin de pouvoir déployer la fonction AWS Lambda que nous allons nommer ssh_execute, les outils ci-dessous sont nécessaires :

  1. Un compte AWS AWS Free Tier

  2. Un éditeur de texte

  3. Python 3.6

  4. pip

  5. Le module Python venv

  6. Git

  7. 7zip

Attention, sous Windows, il est important de ne pas utiliser l'outil d'archivage fourni par défaut, car ce dernier n'est pas supporté par AWS Lambda et la fonction risque de ne pas fonctionner correctement.

Remarque : Les commandes de cet article ont été testées sur la distribution Ubuntu 18.04

Les commandes ci-dessous installent les outils nécessaires sur une distribution Ubuntu 18.04 (à exécuter en root) :

apt update 
apt install python3 python3-venv python-pip p7zip-full git

Mise en place de la fonction Lambda

Récupérer le code source depuis le dépôt github

git clone https://github.com/obounaim/lambda_function_examples.git

Créer un virtualenv Python

Un virtualenv (Virtual Environment) Python permet d'avoir un environnement Python unique. Il est aussi nécessaire pour inclure des bibliothèques Python qui ne sont pas fournies par défaut par l'environnement d'exécution de la fonction Lambda. Les étapes ci-dessous montrent comment le créer.

  1. Se positionner dans le dossier lambda_function_examples/execute_ssh/
cd lambda_function_examples/execute_ssh/
  1. Créer un nouveau virtualenv dans ce dossier
python3 -m venv lambda_venv
  1. Activer le nouveau virtualenv
source lambda_venv/bin/activate

Installer les bibliothèques Python nécessaires pour la fonction Lambda

Les bibliothèques sont définies dans le fichier requirements.txt à la racine du répertoire lambda_function_examples/execute_ssh/.

La bibliothèque "boto3" de AWS est aussi nécessaire pour le fonctionnement de notre code. Cependant, nous n'avons pas besoin de l'installer sur le nouveau virtualenv car elle est fournie par défaut par l'environnement d'exécution de la fonction Lambda.

Le gestionnaire de paquets Python pip sera utilisé pour installer les bibliothèques.

Remarque : Le virtualenv doit être activé avant de lancer la commande pip3

pip install -r requirements.txt

Ignorer l'erreur concernant l'installation des dépendances pycparser et pynacl. Le message d'erreur "Failed building wheel" dit que pip n'arrive pas à convertir les paquets pycparser et pynacl vers le nouveau format wheel. pip continuera avec l'ancienne version Egg (Wheel vs Egg).

Le code source de la fonction AWS Lambda

Après avoir installé les prérequis et configuré le virtualenv, nous pouvons désormais s'intéresser au code de la fonction Lambda.

lambda_function_examples/execute_ssh/
    |
    |-- lambda_venv
    |
    |-- main.py
    |
    |-- requirements.txt
    |
    |-- zip_lambda_function.sh

Le code source de la fonction AWS Lambda dans le fichier main.py est découpé en trois fonctions Python :

  1. handler(event, context)

    La fonction Python handler est le point d'entrée de la fonction Lambda. Le service AWS Lambda invoque la fonction handler pour lancer l'exécution du code Python.
    Les paramètres event et context sont utilisés pour initialiser le code Python :

    event : Ce paramètre, généralement de type dictionnaire permet de transmettre des paramètre concernant l’event. Dans notre cas, il contient les clés de type string comme l'IP de l'instance EC2 le nom de l'utilisateur pour la connexion SSH, la clé privée et la commande à exécuter.

    context : Ce paramètre contient des informations à propos du runtime de l’event, il faut le renseigner, mais il n’est pas utilisée dans notre cas.

  2. connect_ssh(host_ip, host_username, encrypted_private_key)

    Cette fonction se connecte en SSH sur l'IP de l'instance Linux EC2 host_ip avec l'utilisateur host_username et la clé privée encrypted_private_key. L'objet retourné sera utilisé par la fonction execute_ssh_cmd() pour exécuter des commandes sur l'instance Linux EC2.

    Authentification sur l'instance Linux EC2 :

    Par défaut, sur une instance Linux EC2 l'authentification se fait uniquement par une paire de clé publique/privée SSH. La clé publique doit être copiée dans le fichier .ssh/authorized_keys dans le répertoire home de l'utilisateur utilisé pour se connecter à l'instance Linux EC2. La clé privée est utilisée pour s'authentifier sur l'instance Linux EC2. Méthode générique pour configurer une authentification SSH

    Pour protéger votre clé privée, vous pouvez utiliser le service AWS KMS et la chiffrer avant de la passer à votre fonction Lambda. Je ne détaille pas la création de votre clé KMS dans cet article.

    La sortie de la commande ci-dessous doit être passée à la fonction Lambda dans la clé encrypted_private_key de la variable de type dict event de la fonction handler.

    PATH représente le chemin vers la clé privée SSH. KEY-ID représente l'ID de la clé CMK.

    aws kms encrypt --key-id KEY-ID --plaintext fileb://PATH --output text --query CiphertextBlob
    

    Remarque: La fonction Lambda doit avoir les droits IAM nécessaires pour déchiffrer la clé SSH privée l'aide de la clé CMK

  3. execute_ssh_cmd(ssh_client, cmd)

    Cette fonction utilise l'objet retourné par la fonction connect_ssh() pour lancer la commande cmd.

Importer la fonction Lambda

  1. Créer une archive zip qui contient le code Python ainsi que les bibliothèques Python nécessaires.

    bash zip_lambda_function.sh

  2. Créer la fonction Lambda et importer le fichier zip

    1. Créer une nouvelle fonction Lambda et importer le fichier zip sur la console web AWS

    2. Configurer la fonction Lambda dans un subnet du VPC de l'instance EC2. Le subnet doit être privé avec une route vers la NAT gateway afin que la fonction Lambda puisse invoquer l'API publique de AWS KMS
      (Une alternative à la NAT Gateway est possible en utilsant VPC KMS Endpoint).

    3. Configurer les Security Groups de la fonction Lambda pour autoriser la connexion SSH vers l'instance EC2

    4. Configurer un rôle IAM pour autoriser l'accès à la clé CMK sur le service KMS pour déchiffrer la clé SSH privée.

    5. Lancer un test avec ce paramètre event (en reiseignant vos propres informations) :

    {
        "username": "",
        "ip": "",
        "encrypted_private_key" : ""
        "cmd": "date"
    }
    

Architecture du fonctionnement de la fonction Lambda

Conclusion

La fonction Lambda présentée peut être étendue pour servir de base pour les scénarios suivants :

  1. Configurer un nouveau serveur en lançant un Playbook ansible

  2. Récupérer des fichiers (Logs d'accès, images, ...) et les stocker sur S3

  3. Lancer une tâche d'administration (Redémarrer un service, vider un cache, ...)

  4. Lancer une tâche récurrente sur un ensemble d'instances Linux EC2 à partir d'un point central

Il est aussi important de revenir sur la criticité de la gestion de la clé ssh privée et des mots de passe d'une manière générale. L'utilisation d'une clé KMS pour chiffrer les secrets est fortement recommandée, passer des secrets en clair présente un risque de sécurité non-négligeable.

Pour plus d'informations sur le service AWS Lambda :

  1. Site AWS

  2. Article de blog WeScale

  3. re:Invent

  4. Tarifs