Connecter des applications à des instances à l'aide de SSH

Si vous utilisez des comptes de service sur vos instances pour exécuter des tâches automatisées et interagir avec d'autres API Google Cloud Platform, ce compte de service peut également nécessiter un accès SSH à d'autres instances de Compute Engine. Ce tutoriel explique comment configurer des applications pour accéder à vos instances via des connexions SSH. L'exemple d'application présenté dans ce tutoriel utilise un compte de service et OS Login pour la gestion des clés SSH.

Pour ignorer cet exercice et voir l'exemple de code complet, consultez la page GoogleCloudPlatform / python-docs-samples sur GitHub.

Objectifs

Ce tutoriel vous explique comment atteindre les objectifs suivants :

  • Créer un compte de service et le configurer pour fournir un accès SSH OS Login aux applications qui se connectent à vos instances.
  • Créer une instance associée à votre compte de service.
  • Configurer l'exemple d'application sur votre instance pour utiliser le compte de service afin de gérer ses propres clés SSH et d'établir des connexions SSH.
  • Exécuter l'application sur une instance à laquelle le compte de service est associé.
  • Exécuter l'application en dehors d'une instance Compute Engine où vous devez indiquer manuellement la clé de compte de service et spécifier des paramètres SSH supplémentaires.

Coûts

Ce tutoriel utilise des composants facturables de Cloud Platform, tels que Compute Engine.

Les nouveaux utilisateurs de Cloud Platform peuvent bénéficier d'un essai gratuit.

Avant de commencer

  1. Connectez-vous à votre compte Google.

    Si vous n'en possédez pas déjà un, vous devez en créer un.

  2. Sélectionnez ou créez un projet Google Cloud Platform.

    Accéder à la page "Gérer les ressources"

  3. Assurez-vous que la facturation est activée pour votre projet Google Cloud Platform.

    Découvrir comment activer la facturation

  4. Votre compte utilisateur doit être autorisé à créer, supprimer et modifier plusieurs ressources Compute Engine. Dans ce tutoriel, nous partons du principe que vous disposez des rôles IAM suivants pour votre projet.
    • compute.instanceAdmin.v1
    • compute.networkAdmin
    • compute.osAdminLogin
    • iam.serviceAccountAdmin
    • iam.serviceAccountKeyAdmin
    • iam.serviceAccountUser
  5. Pour ce tutoriel, nous partons du principe que vous utilisez Cloud Shell pour exécuter les commandes gcloud.

Créer et configurer le compte de service et les exemples d'instances

Ce tutoriel utilise un compte de service et deux instances pour montrer comment vos applications peuvent exécuter des commandes SSH sur des instances distantes.

Utilisez les commandes suivantes pour configurer l'environnement de test :

  1. Ouvrez Cloud Shell dans la console :

    Ouvrir Cloud Shell

  2. Exportez une variable d'environnement pour définir votre ID de projet pour les commandes futures :

    export PROJECT_ID='[PROJECT_ID]'
    
  3. Créez un compte de service dans votre projet. Pour cet exemple, créez un compte de service nommé ssh-account :

    gcloud iam service-accounts create ssh-account --project $PROJECT_ID \
       --display-name "ssh-account"
    
  4. Créez un réseau temporaire nommé ssh-example à utiliser uniquement pour cet exemple :

    gcloud compute networks create ssh-example --project $PROJECT_ID
    
  5. La commande create crée une règle de pare-feu qui autorise toutes les connexions SSH aux instances du réseau ssh-example :

    gcloud compute firewall-rules create ssh-all --project $PROJECT_ID \
       --network ssh-example --allow tcp:22
    
  6. Créez une instance dans la zone us-central1-f nommée target. Cette instance sert d'instance distante à laquelle votre compte de service se connectera via SSH. OS Login doit être activé au niveau du projet ou de l'instance. Cet exemple montre comment utiliser l'indicateur --metadata pour activer OS Login sur cette instance spécifique. Incluez les indicateurs --no-service-account et --no-scopes, car cette instance n'a pas besoin d'exécuter de requête API pour cet exemple spécifique :

    gcloud compute instances create target --project $PROJECT_ID \
       --zone us-central1-f --network ssh-example \
       --no-service-account --no-scopes \
       --machine-type f1-micro --metadata=enable-oslogin=TRUE
    
  7. Accordez le rôle IAM compute.osAdminLogin au compte de service afin qu'il puisse établir spécifiquement des connexions SSH avec l'instance nommée target. Le rôle compute.osAdminLogin accorde également à votre compte de service les privilèges de super-utilisateur sur l'instance. Bien que vous puissiez accorder ce rôle au niveau du projet afin qu'il s'applique à toutes les instances de votre projet, accordez-le spécifiquement au niveau de l'instance pour limiter les autorisations dans cet exemple :

    gcloud compute instances add-iam-policy-binding target \
       --project $PROJECT_ID --zone us-central1-f \
       --member serviceAccount:ssh-account@$PROJECT_ID.iam.gserviceaccount.com \
       --role roles/compute.osAdminLogin
    
  8. Créez une instance dans la zone us-central1-f nommée source. Associez l'instance au compte de service ssh-account. Spécifiez également le champ d'application de cloud-platform. Cette action est nécessaire pour que le compte de service exécute les requêtes API sur cette instance :

    gcloud compute instances create source \
       --project $PROJECT_ID --zone us-central1-f \
       --service-account ssh-account@$PROJECT_ID.iam.gserviceaccount.com  \
       --scopes https://www.googleapis.com/auth/cloud-platform \
       --network ssh-example --machine-type f1-micro
    

Le compte de service peut désormais gérer ses propres paires de clés SSH et utiliser SSH pour se connecter spécifiquement à l'instance target. L'instance source étant associée au compte de service ssh-account que vous avez créé, la bibliothèque cliente Python peut utiliser les identifiants par défaut de l'application pour s'authentifier en tant que compte de service et utiliser les rôles que vous avez précédemment attribués à ce compte de service.

Ensuite, configurez et exécutez une application qui peut passer d'une instance à l'autre avec un accès SSH.

Exécuter une application SSH sur une instance

Lorsque les applications exécutées sur vos instances nécessitent un accès SSH à d'autres instances, vous pouvez gérer les paires de clés SSH de votre compte de service et exécuter des commandes SSH par programmation. Pour cet exemple, exécutez un exemple d'application en utilisant le processus suivant :

  1. Connectez-vous à l'instance source à l'aide de l'outil de ligne de commande gcloud :

    gcloud compute ssh source --project $PROJECT_ID --zone us-central1-f
    
  2. Sur l'instance source, installez pip et la bibliothèque du client Python :

    my-username@source:~$ sudo apt update && sudo apt install python-pip -y && pip install --upgrade google-api-python-client
    
  3. Téléchargez l'exemple d'application service_account_ssh.py partir de GoogleCloudPlatform/python-docs-samples :

    my-username@source:~$ curl -O https://raw.githubusercontent.com/GoogleCloudPlatform/python-docs-samples/master/compute/oslogin/service_account_ssh.py
    
  4. Exécutez l'exemple d'application qui utilise argparse pour accepter les variables depuis la ligne de commande. Dans cet exemple, indiquez à l'application d'installer et d'exécuter cowsay sur l'instance target. Pour cette commande, ajoutez votre ID de projet manuellement :

    my-username@source:~$ python service_account_ssh.py \
        --cmd 'sudo apt install cowsay -y && cowsay "It works!"' \
        --project [PROJECT_ID] --zone us-central1-f --instance target
    
    ⋮
    ___________
      It works!
    -----------
           \   ^__^
            \  (oo)\_______
               (__)\       )\/\
                   ||----w |
                   ||     ||
    
    

Si l'application s'exécute correctement, vous recevrez le résultat de l'application cowsay. Vous pouvez modifier l'indicateur --cmd pour inclure toutes les commandes que vous souhaitez. Vous pouvez également écrire votre propre application qui importe service_account_ssh.py et l'appelle directement.

Exécutez exit pour vous déconnecter de l'instance source et revenir à Cloud Shell.

Exécuter une application SSH en dehors de Compute Engine

Dans l'exemple précédent, vous avez exécuté l'application sur une instance Compute Engine sur laquelle la bibliothèque cliente Python peut utiliser les identifiants par défaut de l'application pour utiliser le compte de service associé à l'instance source. Si vous exécutez cette application en dehors d'une instance Compute Engine, la bibliothèque cliente ne peut pas accéder au compte de service et à ses autorisations à moins que vous n'indiquiez manuellement la clé du compte de service.

  1. Obtenez l'adresse IP externe de l'instance target créée précédemment dans ce tutoriel. Vous pouvez trouver cette adresse dans la console sur la page Instances ou en exécutant la commande suivante à partir de l'outil de ligne de commande gcloud :

    gcloud compute instances describe target \
       --project $PROJECT_ID --zone us-central1-f
    
  2. Créez une clé de compte de service pour le service ssh-account que vous avez utilisé dans l'exemple précédent et téléchargez le fichier de clé sur votre poste de travail local.

  3. Copiez la clé du compte de service sur le système sur lequel vous souhaitez exécuter cet exemple.

  4. Ouvrez un terminal sur le système sur lequel vous souhaitez exécuter cet exemple.

  5. Définissez la variable d'environnement GOOGLE_APPLICATION_CREDENTIALS pour qu'elle pointe vers le chemin où se trouve le fichier .json de clé de compte de service. Si votre clé se trouve dans votre dossier Downloads, vous pouvez définir une variable d'environnement semblable à l'exemple suivant :

    $ export GOOGLE_APPLICATION_CREDENTIALS="/home/user/Downloads/key.json"
    
  6. Installez les prérequis sur ce système :

    1. Installez Python et pip : Sur les systèmes basés sur Debian, vous pouvez utiliser apt pour effectuer cette étape :

      $ sudo apt update && sudo apt install python python-pip -y
      
    2. Utilisez pip pour installer la bibliothèque cliente Python :

      $ pip install --upgrade google-api-python-client
      
  7. Téléchargez l'exemple d'application :

    $ curl -O https://raw.githubusercontent.com/GoogleCloudPlatform/python-docs-samples/master/compute/oslogin/service_account_ssh.py
    
  8. Exécutez l'exemple d'application. Lorsque vous exécutez l'application en dehors de Compute Engine, le serveur de métadonnées n'est pas disponible. Vous devez donc spécifier manuellement l'adresse de messagerie du compte de service. Vous devez également spécifier l'adresse IP externe de l'instance target obtenue précédemment.

    $ python service_account_ssh.py \
        --cmd 'sudo apt install cowsay -y && cowsay "It works!"' \
        --account ssh-account@[PROJECT_ID].iam.gserviceaccount.com \
        --project [PROJECT_ID] --hostname [TARGET_EXTERNAL_IP]
    
    ⋮
    ___________
      It works!
    -----------
           \   ^__^
            \  (oo)\_______
               (__)\       )\/\
                   ||----w |
                   ||     ||
    
    

Si l'application s'exécute correctement, vous recevrez le résultat de l'application cowsay.

Fonctionnement de l'exemple d'application

L'exemple d'application service_account_ssh.py fonctionne à l'aide du processus suivant :

  1. Initialisez l'objet API OS Login.
  2. Si vous n'indiquez pas l'adresse électronique du compte de service manuellement, l'application lit les métadonnées de l'instance pour identifier le compte de service associé à l'instance. Si vous exécutez cette application en dehors de Compute Engine, vous devez indiquer l'adresse du compte de service manuellement.
  3. Appelez la méthode create_ssh_key() pour générer une clé SSH temporaire pour le compte de service sur l'instance sur laquelle cet exemple est exécuté et ajoutez la clé publique au compte de service avec un timer d'expiration que vous pouvez spécifier.
  4. Appelez la méthode getLoginProfile() à partir de l'API OS Login pour obtenir le nom d'utilisateur POSIX utilisé par le compte de service.
  5. Appelez la méthode run_ssh() pour exécuter une commande SSH distante en tant que compte de service.
  6. Imprimez la réponse à partir de la commande SSH distante.
  7. Supprimez les fichiers de clé SSH temporaires.
  8. OS Login supprime automatiquement les fichiers de clé publique lorsqu'ils dépassent le délai d'expiration.
def main(cmd, project, instance=None, zone=None,
         oslogin=None, account=None, hostname=None):
    """Run a command on a remote system."""

    # Create the OS Login API object.
    oslogin = oslogin or googleapiclient.discovery.build('oslogin', 'v1')

    # Identify the service account ID if it is not already provided.
    account = account or requests.get(
        SERVICE_ACCOUNT_METADATA_URL, headers=HEADERS).text
    if not account.startswith('users/'):
        account = 'users/' + account

    # Create a new SSH key pair and associate it with the service account.
    private_key_file = create_ssh_key(oslogin, account)

    # Using the OS Login API, get the POSIX user name from the login profile
    # for the service account.
    profile = oslogin.users().getLoginProfile(name=account).execute()
    username = profile.get('posixAccounts')[0].get('username')

    # Create the hostname of the target instance using the instance name,
    # the zone where the instance is located, and the project that owns the
    # instance.
    hostname = hostname or '{instance}.{zone}.c.{project}.internal'.format(
        instance=instance, zone=zone, project=project)

    # Run a command on the remote instance over SSH.
    result = run_ssh(cmd, private_key_file, username, hostname)

    # Print the command line output from the remote instance.
    # Use .rstrip() rather than end='' for Python 2 compatability.
    for line in result:
        print(line.decode('utf-8').rstrip('\n\r'))

    # Shred the private key and delete the pair.
    execute(['shred', private_key_file])
    execute(['rm', private_key_file])
    execute(['rm', private_key_file + '.pub'])

if __name__ == '__main__':

    parser = argparse.ArgumentParser(
        description=__doc__,
        formatter_class=argparse.RawDescriptionHelpFormatter)
    parser.add_argument(
        '--cmd', default='uname -a',
        help='The command to run on the remote instance.')
    parser.add_argument(
        '--project',
        help='Your Google Cloud project ID.')
    parser.add_argument(
        '--zone',
        help='The zone where the target instance is locted.')
    parser.add_argument(
        '--instance',
        help='The target instance for the ssh command.')
    parser.add_argument(
        '--account',
        help='The service account email.')
    parser.add_argument(
        '--hostname',
        help='The external IP address or hostname for the target instance.')
    args = parser.parse_args()

    main(args.cmd, args.project, instance=args.instance, zone=args.zone,
         account=args.account, hostname=args.hostname)

La méthode create_ssh_key() génère une nouvelle paire de clés SSH. Ensuite, la méthode appelle users().importSshPublicKey() partir de l'API OS Login pour associer la clé publique au compte de service. La méthode users().importSshPublicKey() accepte également une valeur d'expiration, qui indique la durée pendant laquelle la clé publique reste valide.

def create_ssh_key(oslogin, account, private_key_file=None, expire_time=300):
    """Generate an SSH key pair and apply it to the specified account."""
    private_key_file = private_key_file or '/tmp/key-' + str(uuid.uuid4())
    execute(['ssh-keygen', '-t', 'rsa', '-N', '', '-f', private_key_file])

    with open(private_key_file + '.pub', 'r') as original:
        public_key = original.read().strip()

    # Expiration time is in microseconds.
    expiration = int((time.time() + expire_time) * 1000000)

    body = {
        'key': public_key,
        'expirationTimeUsec': expiration,
    }
    oslogin.users().importSshPublicKey(parent=account, body=body).execute()
    return private_key_file

Il est recommandé de configurer vos comptes de service afin qu'ils génèrent régulièrement de nouvelles paires de clés pour eux-mêmes Dans cet exemple, le compte de service crée une nouvelle paire de clés pour chaque connexion SSH qu'il établit, mais vous pouvez le modifier pour qu'il s'exécute selon un calendrier spécifique répondant mieux aux besoins de votre application.

Le corps de la requête pour users().importSshPublicKey() inclut la valeur expirationTimeUsec, qui indique à OS Login lorsque la clé doit expirer. Chaque compte ne pouvant contenir que 32 Ko de données maximum de clé SSH, il est donc préférable de configurer vos clés SSH publiques de manière à ce qu'elles expirent peu de temps après la fin des opérations de votre compte de service.

Une fois que votre compte de service a configuré ses clés SSH, il peut exécuter des commandes à distance. Dans cet exemple, l'application utilise la méthode run_ssh() pour exécuter une commande sur une instance distante et renvoyer le résultat de la commande.

def run_ssh(cmd, private_key_file, username, hostname):
    """Run a command on a remote system."""
    ssh_command = [
        'ssh', '-i', private_key_file, '-o', 'StrictHostKeyChecking=no',
        '{username}@{hostname}'.format(username=username, hostname=hostname),
        cmd,
    ]
    ssh = subprocess.Popen(
        ssh_command, shell=False, stdout=subprocess.PIPE,
        stderr=subprocess.PIPE)
    result = ssh.stdout.readlines()
    return result if result else ssh.stderr.readlines()

Effectuer un nettoyage

Pour éviter que les ressources utilisées dans ce tutoriel soient facturées sur votre compte Google Cloud Platform :

Utilisez les commandes suivantes pour nettoyer les ressources de votre environnement de test :

  1. Ouvrez Cloud Shell dans la console :

    Ouvrir Cloud Shell

  2. Supprimez l'instance nommée source :

    gcloud compute instances delete source \
       --project $PROJECT_ID --zone us-central1-f
    
  3. Supprimez l'instance nommée target :

    gcloud compute instances delete target \
       --project $PROJECT_ID --zone us-central1-f
    
  4. Supprimez le compte de service ssh-account :

    gcloud iam service-accounts delete ssh-account --project $PROJECT_ID
    
  5. Supprimez le réseau nommé ssh-example :

    gcloud compute networks delete ssh-example --project $PROJECT_ID
    

Étapes suivantes

Cette page vous a-t-elle été utile ? Évaluez-la :

Envoyer des commentaires concernant…

Documentation Compute Engine