Net Core + VS Code => Débugage à distance dans un conteneur par tunnel ssh? Why not!

Net Core + VS Code => Débugage à distance dans un conteneur par tunnel ssh? Why not!

2018-07-29 0 By Nordes

Idée de la tâche à accomplir

Comme vous le savez, des fois il est quasi impossible de faire du débugage local. C’est à ce moment que l’on doit se pencher sur le débugage à distance de processus. Dans tous les cas, faire ça à distance et éviter de se baser que sur les logs et les suppositions, n’est jamais simple. Microsoft à fait quelque chose de vraiment bien en emmenant la possibilité de le faire sur quasi toutes les plateformes dont on peut déployer. De plus Visual Studio Code fonctionne partout (merci Electron). Ici, avec .Net Core 2.1, nous allons debugger sur de Windows vers Linux (ubuntu). Veuillez noter que ça ne s’arrête pas que là et c’est à vous de vous amuser avec vos limitations (des fois proxy, firewall, etc.).

Le but de cet article sera d’utiliser Visual Studio Code, créer un projet et le déployer sur un hôte situé dans un conteneur LXD sur un serveur Linux Ubuntu. Du coup, le schéma ressemble un peu à ce qui suit:

Notre objectif étant de se connecter au conteneur final dans un environnement très sécurisé, nous aurons besoin de:

  • Visual Studio Code (Pourrait aussi éventuellement être Visual Studio 2017)
    • Extension C# .Net (OmniSharp)
  • Git Bash, ou un bash ou vous devrez pouvoir faire de la redirection SSH. (Tunnel)
    • plink peut sans doute aussi fonctionné
  • Un serveur Ubuntu qui exécute des conteneur de type LXD

Évidemment je m’attends de vous que vous aillez quelques connaissance sur Linux, Windows ainsi que sur les clés sécurisé SSH.

Séparation de l’article

  • Qu’est-ce que LXD?
  • Création d’une application .Net Core qui sera utilisé pour votre premier débugage à distance.
  • Création d’un tunnel locale
  • Utilisation de la clé SSH pour la connection au tunnel (Conteneur)
  • Installation de VSDBG
  • Configuration de VS Code (Launch.json)
  • Déploiement de l’application
  • Démarrage de l’application
  • S’attacher au processus par Visual Studio Code
    • Le point d’arrêt devra être touché au moment de l’exécution de ce dernier
  • Conclusion

Qu’est-ce que LXD

Ceci est une question qui à le mérite d’être répondu. Cependant, si vous êtes sur cet article et que vous savez déjà ce qu’est LXD. Veuillez, passer à la prochaine étape.

LXD est tout simplement une solution de conteneur (containter), tout comme Docker, natif sur Ubuntu. Pour les détails complets, vous pouvez aller sur le site officiel de la documentation.

Pour tester un accès à votre conteneur en bash, vous pouvez faire “lxc exec mon-conteneur bash” et du coup vous allez tomber dans le bash de votre conteneur et vous assurer que vous avez un serveur SSH accessible. Lors de votre déploiement, si vous vous connecté par sFTP, le lieu ou résidera le conteneur sera situé sous le répertoire /var/lib/lxd/container/mon-conteneur/fsroot/…/…”.

La solution proposé dans cet article s’adresse à LXD, mais si vos conteneurs Docker ne sont pas directement exposé vers l’extérieur, vous aurez le même processus ou voir très semblable à exécuter.

Création d’une application .Net Core qui sera utilisé pour votre premier débugage à distance

Il y a deux façon de procéder. La première est d’utiliser la ligne de commande et la seconde, la plus populaire, utiliser Visual Studio 2017 et utilisé le template pour créer un projet. Pour vous montrer à quel point c’est simple avec .Net Core 2.1, dans cet article nous procéderons avec la lignes de commandes. Donc, si vous préférez Visual Studio 2017, veuillez sauter quelques étapes ;).

  1. Ouvrir une invite de commande (prompt) ou bien à partir de VS Code ouvrir votre répertoire de démo/test
  2. Déplacez-vous dans votre répertoire de choix si ce n’est pas déjà fait. Dans l’exemple ici présent on prendra c:/RemoteDebugging/
  3. Création du projet de type WebApi (.Net Core 2.1): dotnet new webapi (Si vous voulez, vous pouvez aussi faire un projet de console avec une boucle infinie)
    1. eEUne fois créé, testez en lançant la commande: dotnet run
    2. Le service web démarrera. Depuis la version 2.1, le SSL est donné par défaut. Si vous le voulez, vous pouvez toujours le désactiver.  L’API sera accessible sur: https://localhost:5001/api/values
    3. Un document JSON vous sera affiché et nous pouvons passer à la prochaine étape
    4. Vous devrez aussi stopper votre serveur web (CTRL+C), car pour publier ça ne fonctionnera pas si vous êtes attaché au processus ;).
    5. Exemple de ce que ça donnera (si vous êtes sur un Mac)
      Mac-Air:RemoteDebugging nordes$ dotnet new webapi
      The template "ASP.NET Core Web API" was created successfully.
      
      Processing post-creation actions...
      Running 'dotnet restore' on /Users/nordes/programs/RemoteDebugging/RemoteDebugging.csproj...
        Restoring packages for /Users/nordes/programs/RemoteDebugging/RemoteDebugging.csproj...
        Generating MSBuild file /Users/nordes/programs/RemoteDebugging/obj/RemoteDebugging.csproj.nuget.g.props.
        Generating MSBuild file /Users/nordes/programs/RemoteDebugging/obj/RemoteDebugging.csproj.nuget.g.targets.
        Restore completed in 6.94 sec for /Users/nordes/programs/RemoteDebugging/RemoteDebugging.csproj.
      
      Restore succeeded.
      
      Mac-Air:RemoteDebugging nordes$ ls
      Controllers			Properties			Startup.cs			appsettings.json		wwwroot
      Program.cs			RemoteDebugging.csproj		appsettings.Development.json	obj
      Mac-Air:RemoteDebugging nordes$ dotnet run
      Using launch settings from /Users/nordes/programs/RemoteDebugging/Properties/launchSettings.json...
      : Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0]
            User profile is available. Using '/Users/nordes/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest.
      info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[58]
            Creating key {cf1e32d1-02af-4459-96ab-635d858c7a62} with creation date 2018-07-28 00:02:46Z, activation date 2018-07-28 00:02:46Z, and expiration date 2018-10-26 00:02:46Z.
      warn: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[35]
            No XML encryptor configured. Key {cf1e32d1-02af-4459-96ab-635d858c7a62} may be persisted to storage in unencrypted form.
      info: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[39]
            Writing data to file '/Users/nordes/.aspnet/DataProtection-Keys/key-cf1e32d1-02af-4459-96ab-635d858c7a62.xml'.
      Hosting environment: Development
      Content root path: /Users/nordes/programs/RemoteDebugging
      Now listening on: https://localhost:5001
      Now listening on: http://localhost:5000
      Application started. Press Ctrl+C to shut down.
  4. À partir de c:/RemoteDebugging (console), vous pouvez ouvrir Visual studio code en exécutant la commande code . . Si vous n’êtes pas sur Windows, par exemple, sur Mac, simplement ouvrir le répertoire à partir de VS Code.
  5. Une fois dans VS Code, veuillez faire un build (ctrl+shift+p > build et choisir .Net core) directement à partir de l’interface. Ou sinon faire CTRL+Shift+B (Build), normalement vous aurez la création des fichiers .vscode/launch.json et .vscode/tasks.json

En alternative, à partir de Visual Studio 2017, créer un nouveau projet WebApi sous .Net Core 2.1. N’oubliez pas que l’article est pour VS Code en remote débugging. À partir de Visual Studio 2017, il y a tout plein d’article déjà sur ce sujet. L’expérience finale est sensiblement la même, mais moins utile si vous utilisez des ordinateurs distant ou des PC de gens qui n’ont pas de license VS 2017. Dans mon cas, j’ai toujours eu des soucis lors de sessions de debuggage sous VS 2017 en remote debugging.

Création d’un tunnel locale

Le tunnel va vous permettre d’atteindre directement le serveur de destination exécutant votre application. Ceci évitera d’exposer un port SSH de votre conteneur au grand public. Côté sécurité, on préférera toujours un seul point d’entré en évitant d’exposé tous les conteneurs directement au public. Dans une architecture micro-service par exemple, j’aurais un serveur NGinX qui s’occuperait de rediriger mes requêtes vers mes conteneurs sans avoir d’autre point d’accès que l’API ou le Web.

Du coup, afin d’éviter une connection directe sur votre conteneur, vous devrez effectuer un Tunnel SSH. Le tunnel sera important, car pour faire le débuggage, vous devrez vous attacher au processus et ce d’une façon directe. Le tunnel vous permet d’éviter de faire une double connection et vous donne un accès direct au processus en cours d’exécution.

Pour cet article, nous allons nous connecter sur localhost:22222, qui pourrait d’ailleurs utiliser n’importe quel port. Ce sera notre porte d’entrée du tunnel SSH. Ce tunnel comme vous le voyez dans le schéma, effectuera la connection vers la destination finale 10.128.10.123:22 (Conteneur) qui lui même est accessible seulement à partir du serveur Ubuntu (svr-ubuntu:22).

En résumé, et de façon textuel, ça ressemblera à ceci:

  • VS Code => [plink|ssh] => [Tunnel SSH Local (22222) => svr-ubuntu:22 => ip-de-conteneur:22 => attacher au processus de votre application]

Démarrer votre Tunnel

Ouvrez Git Bash (ssh est accessible automatiquement à partir de là, donc créer le tunnel sera juste trop simple)

Commande: ssh -L 22222:10.128.10.123:22 monUtilisateur@svr-ubuntu

Veuillez prendre pour acquis, qu’à l’intérieur du serveur, votre clé SSH est utilisé afin d’atteindre la destination finale.

Utilisation de la clé SSH pour la connection au tunnel (Conteneur)

Cette partie vous permet d’éviter d’inscrire un mot de passe lors de la connection au serveur final (Conteneur). Si vous passez cette étape, vous pourrez bien avoir une erreur de connection lors du débuggage à distance.

  1. Se connecter à votre serveur svr-ubuntu avec “monUtilisateur”
  2. Téléchargez votre clé SSH ~/.ssh/id_rsa (ou extraire ses données en effectuant cat ~/.ssh/id_rsa et sélectionner le résultat de la sortie. Ensuite, copier son contenu dans un fichier texte..
  3. Mettre le fichier dans un endroit quelconque que vous allez ensuite vous rappeler (i.e.: c:/secure_rsa/svr-ubuntu_rsa)
  4. Ouvrir PuttyGen et ensuite charger le fichier (1) c:/secure_rsa/svr-ubuntu_rsa. Par la suite, le logiciel vous demandera de changer le type de fichier à quelque chose de plus récent, à ce moment, acceptez et vous pourrez créer votre fichier “private public key” (PPK). Sauvegardez ce nouveau fichier sous un nom différent (2). Dans mon cas : c:/secure_rsa/myserver.ppk.

Tester votre tunnel SSH

C’est bon, vous pouvez tester votre tunnel SSH. Pour ce faire, veuillez exécuter la commande suivante (À partir de Git Bash):

ssh monUtilisateur@localhost -p 22222 -i c:/secure_rsa/svr-ubuntu_rsa

Si vous le voulez, vous pouvez aussi tester directement avec PLink (putty link). Cet outil permet de faire du ssh directement dans Windows. C’est d’ailleurs ce que nous utiliserons dans notre configuration Visual Studio Code.

Installation de VSDBG

Commande officielle pour installer le vsdbg sur un conteneur lxd:

curl -sSL https://aka.ms/getvsdbgsh | bash /dev/stdin -v latest -l ~/vsdbg

Configuration de VS Code (Launch.json)

Plus tôt, vous avez eu a créer le fichier launch.json. Si ce fichier n’existe toujours pas, veuillez le créer (c:/RemoteDebugging/.vscode/launch.json). Ici nous allons utiliser PLink car on peut le faire. Sur Linux, Mac ou bien sous Windows 10 avec le module SSH installé, on pourrait utiliser le tunnel SSH directement de par la commande lancé ici. D’ailleurs je crois que plink permet aussi de lancer le tunnel de par ici directement, donc une des étapes précédente ne serait pas nécessaire.

Ce que nous allons faire ici, c’est simplement ajouter une configuration de build afin de s’attacher à un processus distant. Étant donné que nous ne voulons pas nous attacher directement aux processus du svr-ubuntu, nous allons nous connecter à notre tunnel SSH précédemment configuré.

Voici donc la configuration:

{
    "name": ".NET Core Remote Attach",
    "type": "coreclr",
    "request": "attach",
    "processId": "${command:pickRemoteProcess}",
    "pipeTransport": {
        "pipeProgram": "plink",
        "pipeArgs": [ "-ssh", "-l", "root", "-pw", "monUtilisateur", "-P", "22222", "localhost", "-i", "C:/secure_rsa/svr-ubuntu.ppk" ],
        "debuggerPath": "~/vsdbg/vsdbg",
        "pipeCwd": "${workspaceRoot}",
        "quoteArgs": true
    },
    "sourceFileMap": {
        "/home/ExampleAccount/ExampleProject": "${workspaceRoot}"
    }
}
  • Pour le lien de source, c’est utile si vous avez une machine qui fait du déploiement automatique et que vous voulez faire du remote debugging. Grosso-modo, ça indiquera de faire l’échange du chemin de la machine build vers votre pc local. (La documentation sur cette partie est accessible sur le site officiel d’omnisharp)

Ressources utiles:

Déploiement de l’application

En principe, vous devrez mettre votre application sur le conteneur et la démarrer. Ici on ne traite pas de comment créer un conteneur et du coup, je vous indique comment faire votre build le plus simplement du monde afin de pouvoir l’exécuter sans problème:

dotnet publish RemoteDebugging.csproj -c Debug --self-contained -r ubuntu-x64

En principe, les pdb seront inclus dans votre publication. Si ce n’est pas le cas, veuillez ajouter les “portable library/is portable” dans votre configuration de projet (csproj).

Une fois la publication terminée, vous pouvez prendre les fichiers du répertoire de publication et transférer par FTP dans votre conteneur (quelque chose comme /var/lib/lxd/container/mon-conteneur/rootfs/usr/lib/mon-applicationtest).

Exécution de l’application

Une fois déployés et les permissions ajusté (utilisateur conteneur + changer en mode executable le fichier “RemoteDebugging”) vous pourrez exécuter votre application sur le conteneur LXD. (Exemple d’accès au conteneur à partir de votre Ubuntu: lxc exec mon-conteneur bash). Pour exécuter l’application, seulement aller dans votre répertoire /usr/lib/mon-applicationtest et exécuter la commande ./RemoteDebugging

Le service WebApi devrait être actif en mode console (ça pourrait aussi être exécuter en tant que service avec systemctl. Ceci sera peut-être pour un autre article).

Vous pouvez tester votre service WebApi avec une autre console sur le serveur ubuntu/conteneur qui essaie d’atteindre http://ip-conteneur/api/values . Normalement, vous devriez avoir un peu de json en sortie.

Ok, ça fonctionne! Maintenant la finalité arrive!

S’attacher au processus par Visual Studio Code

Ici, rien de plus simple. Considérant que vous avez configuré votre launch.json, vous pourrez simplement aller dans l’onglet “debug” de visual studio code et sélectionner votre configuration de “.NET Core Remote Attach”, appuyer sur la flèche pour démarrer. Une connection se fera vers votre Tunnel local pour ensuite se connecter au serveur final (avec la clé ppk).

Ajouter un point d’arrêt et debugger en toute tranquillité

Maintenant, ajoutez un point d’arrêt dans votre contrôleur. Une fois fait, ré-exécuter l’appel http de l’étape précédente. Et hop! Magie, votre point d’arrêt est activé.

Bon debugage!

Conclusion

J’espère bien que cet article vous à plus. Merci à l’avance de laisser un commentaire. Il y a moyen de vraiment simplifier la procédure. Ceci n’était qu’une preuve de concept et dans mon cas, étant derrière des serveurs proxy et firewall, à été la seule façon qui à fonctionné. D’ailleurs, pour le service en développement, je n’avais aucun autre moyen de faire, car la dépendance sur des composantes matérielle externe me permettait d’exécuter le code seulement sur la machine aillant toutes les composantes et non sur un pc local.

En gros, les points a noter sont:

  • Utilisation de la clé ssh (id_rsa)
  • Connection par un tunnel
  • Installation du vsdbg sur l’hôte final
  • Exécution/débugage

Enjoy the impossible 😉