3 сентября 2010 г.

SSH туннелирование, при помощи C#

Ситуация: необходимо получить доступ к БД, которая находится на сервере. На первый взгляд ничего сложного: узнаём порт, который слушает сервер БД, и коннектимся. Но сервер БД слушает localhost порт, а провайдер категорически не хочет открывать порт сервера БД на внешку, в чём я его поддерживаю.

Существует множество способов решить данную проблему, и один из них - создание SSH-туннеля между сервером и клиентом.

картинки взяты с какой-то статьи о remote-debugging'е ZendFramework и немного подправлены

Под *nix системами решение простое:
$ ssh -f -N -L 5432:localhost:5432 username@host
-f
оставить соединение в фоне
-N
не выполнять никаких команд
-L
выполнить "проброс" портов в формате port:host:hostport


Для удалённого SSH-соединения во все времена выручал пресловутый PuTTY. Он же и умеет делать SSH-туннели.



Но поставлять стороннее приложение, хоть оно и opensource, с продуктом, только для создания SSH-туннеля и проброски портов - “не спортивно”. Нужно научить наше приложение самому поднимать SSH соединение.

Известно, что в составе библиотек .NET Framework нет инструментов для работы с SSH-протоколом.
После непродолжительного "гугления" можно обнаружить две разработки, которые не требуют от вас много денежных знаков: Granados и SharpSSH.

Что касается первого, то меня огорчило три вещи:
  1. совсем невменяемая документация;
  2. запутанная структура проекта;
  3. возраст (последние изменения датированы апрелем 2004 года);

Второй же, в отмену, имеет и хорошую документацию (+ блог автора, Тамир Гал) и внятную структуру проекта. Хоть новизной так же не блещет. Помимо этого у проекта есть форк SharpSSH2. Отличия от "папки":
  1. переведён на .NET Framework 3.5;
  2. дополнительные настройки компрессии;
  3. дополнительные опции шифрования AES;
  4. пачка мелких "энхансов".
Сам по-себе SharpSSH, по заявлению автора, является ни чем иным, как переписанным JSch проектом с Java на C#.

Для начала работы, нам необходимо подключить библиотеку SharpSSH и её namespace.

using SharpSsh;

Также необходимо будет реализовать простенький интерфейс:
public interface UserInfo
{
    /// <summary>
    /// Returns the user passphrase (passwd for the private key file)
    /// </summary>
    String getPassphrase();

    /// <summary>
    /// Returns the user password
    /// </summary>
    String getPassword();

    /// <summary>
    /// Prompt the user for a password
    /// </summary>
    bool promptPassword(String message);

    /// <summary>
    /// Prompt the user for a passphrase (passwd for the private key file)
    /// </summary>
    bool promptPassphrase(String message);

    /// <summary>
    /// Prompt the user for a Yes/No input
    /// </summary>
    bool promptYesNo(String message);

    /// <summary>
    /// Show messages
    /// </summary>
    void showMessage(String message);
}


Примерно вот так:
public class MyUserInfo : UserInfo
{
    private String passphrase;

    public MyUserInfo (String Passphrase)
    {
        passphrase = Passphrase;
    }

    public String getPassword()
    {
        return null;
    }

    public bool promptYesNo(String str)
    {
        return true;
    }

    public String getPassphrase()
    {
        return passphrase;
    }

    public bool promptPassphrase(String message)
    {
        return true;
    }

    public bool promptPassword(String message)
    {
        return true;
    }

    public void showMessage(String message)
    {
        // MessageBox.Show(message);
        // Console.WriteLine(message);
    }
}

ненужные методы я грубо закрыл return true;

Далее в коде создадим
Session session;
Channel channel;
JSch jsch;
jsch = new JSch();

const String PrivateKeyFile = "id_dsa";
jsch.addIdentity(PrivateKeyFile);

const String host = "host.com";
const String user = "user";
const String passphrase = "Passphrase";

//Create a new SSH session
session = jsch.getSession(user, host, 22);
session.setPortForwardingL(5432, "localhost", 5432);

//Username and password will be given via UserInfo interface.
UserInfo ui = new MyUserInfo(passphrase);
session.setUserInfo(ui);

//Connect to remote SSH server
session.connect();

//Open a new Shell channel on the SSH session
channel = session.openChannel("shell");

//Connect the channel
channel.connect();

//Wait till channel is closed
while (!channel.isClosed())
{
  System.Threading.Thread.Sleep(500);
}

Ну а дальше "причёсываем" код, добавляем try-catch, где надо и т.п.

Получаем малой кровью, очень даже неплохой SSH-туннель, средствами C#.

Комментариев нет:

Отправить комментарий