Диалер SOCKS обеспечивает транспорт; пакет SSH выполняет поверх него рукопожатие SSH. Вызов ssh.Dial после этого создал бы отдельное соединение вместо использования этого транспорта.

Вспомогательная функция в приложении, которое уже использует x/net/proxy и x/crypto/ssh, может вернуть настроенный SSH-клиент:

func dialSSHViaSOCKS(proxyAddr, sshAddr string,
    config *ssh.ClientConfig) (*ssh.Client, error) {
    dialer, err := proxy.SOCKS5("tcp", proxyAddr, nil, proxy.Direct)
    if err != nil {
        return nil, err
    }

    conn, err := dialer.Dial("tcp", sshAddr)
    if err != nil {
        return nil, err
    }

    transport, channels, requests, err :=
        ssh.NewClientConn(conn, sshAddr, config)
    if err != nil {
        conn.Close()
        return nil, err
    }

    return ssh.NewClient(transport, channels, requests), nil
}

Имена пакетов здесь — golang.org/x/net/proxy и golang.org/x/crypto/ssh. proxyAddr обозначает слушатель SOCKS; sshAddr обозначает конечный сервер в виде host:port.

В config укажите обычного пользователя SSH приложения, методы аутентификации и HostKeyCallback. Пакет knownhosts может построить этот callback из доверенного файла known_hosts. Передайте имя хоста назначения в NewClientConn, чтобы сопоставление ключа хоста использовало нужную идентичность SSH.

Аргумент nil не выбирает учётные данные SOCKS. Для разрешённых публичных SSH-назначений запись без аккаунта из списка прокси SOCKS5 может дать proxyAddr, если он разрешает фактический хост и порт SSH. При необходимости передайте proxy.Auth с учётной записью прокси; аутентификация SSH остаётся в config.

NewClient принимает входящие потоки каналов и запросов, которые возвращает NewClientConn, и управляет этой клиентской механикой. Закрывайте возвращённый SSH-клиент, когда его работа завершается. Обработка соединений в эксплуатации требует также ограниченного дозвона и политики рукопожатия; эта вспомогательная функция лишь иллюстрирует передачу транспорта.

Похожие статьи