Часто разработчики 1С сталкиваются с ситуацией, когда код, прекрасно работающий при интерактивном запуске от имени пользователя, начинает сбоить при выполнении в фоновом или регламентном задании. Особенно часто это проявляется при работе с внешними компонентами и COM-объектами, например, с популярной библиотекой WinSCP для работы с SFTP/SCP. Рассмотрим типичный случай: код для подключения к SFTP-серверу работает в пользовательском сеансе, но в регламентном задании падает с ошибкой на строке получения отпечатка ключа сервера.
Пример проблемного кода:
Попытка
session = Новый COMОбъект("WinSCP.Session");
sessionOptions = Новый COMОбъект("WinSCP.SessionOptions");
sessionOptions.HostName = "roadstore.avito.ru";
sessionOptions.UserName = "pan-remont";
sessionOptions.Password = "bhtyjrr6AzDh";
sessionOptions.PortNumber = 522;
// ↓ Ошибка возникает именно на этой строке в фоновом режиме
fingerprint = session.ScanFingerprint(sessionOptions,"SHA-256");
sessionOptions.SshHostKeyFingerprint = fingerprint;
session.Open(sessionOptions);
Исключение
// В журнал регистрации попадает ошибка:
// {ОбщийМодуль...}: Ошибка при вызове метода контекста (ScanFingerprint):
// Произошла исключительная ситуация (WinSCPnet): WinSCP.SessionRemoteException
ЗаписьЖурналаРегистрации("МояПодсистема", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки());
КонецПопытки;
Давайте разберемся, почему так происходит и как это исправить.
Ключ к пониманию проблемы лежит в контексте выполнения кода. Это два совершенно разных сценария:
C:\Users\ИмяПользователя), свои настройки сети, свои права доступа, сохраненные сертификаты и ключи.Local System, Network Service) или специально созданный пользователь (например, USR1CV8).Именно эта разница в правах и окружении и является корнем зла. Системная учетная запись, в отличие от пользовательской, может не иметь:
Local System такого профиля нет, что приводит к ошибкам.Ошибка WinSCP.SessionRemoteException в данном случае, скорее всего, говорит не о проблеме с удаленным сервером, а о локальной проблеме на сервере 1С, связанной с невозможностью установить соединение из-за вышеперечисленных ограничений.
Рассмотрим несколько способов решения, от самого правильного и надежного до элегантных обходных маневров.
Самый правильный подход — запускать службу сервера 1С не под системной учетной записью, а под специально созданным для этого пользователем. Это стандартная практика для обеспечения безопасности и стабильности работы серверных приложений.
Разберем по шагам:
USR1CV8. Задайте ему надежный пароль и установите флаг «срок действия пароля не ограничен».C:\Users\USR1CV8). Это решит потенциальные проблемы с COM-объектами, которые ожидают наличие такого профиля.services.msc), найдите службу «Агент сервера 1С:Предприятия 8.3», откройте ее свойства. На вкладке «Вход в систему» выберите опцию «С указанной учетной записью», введите имя созданного пользователя (например, .\USR1CV8) и его пароль.После этих действий все фоновые и регламентные задания будут выполняться в контексте пользователя USR1CV8, у которого есть свой профиль и которому можно гибко настроить права доступа к сети и файловой системе. Вероятность успешного выполнения кода с COM-объектом WinSCP приблизится к 100%.
Если изменять настройки службы сервера 1С нет возможности или желания, можно пойти другим путем — модифицировать код так, чтобы он не вызывал проблемный метод ScanFingerprint. Этот метод выполняет динамическое сканирование ключа, что удобно, но не всегда необходимо и не всегда безопасно.
Гораздо более надежный подход — заранее узнать отпечаток ключа хоста и жестко прописать его в коде. Это не только решает нашу проблему, но и повышает безопасность, защищая от атаки «человек посередине» (Man-in-the-Middle).
Как это сделать:
roadstore.avito.ru) с помощью графического клиента WinSCP с любой машины. При первом подключении программа покажет диалоговое окно с предупреждением о неизвестном ключе и отобразит его отпечаток (fingerprint). Скопируйте его.ScanFingerprint и напрямую присвойте полученный отпечаток свойству SshHostKeyFingerprint.Посмотрим на пример измененного кода:
session = Новый COMОбъект("WinSCP.Session");
sessionOptions = Новый COMОбъект("WinSCP.SessionOptions");
sessionOptions.HostName = "roadstore.avito.ru";
sessionOptions.UserName = "pan-remont";
sessionOptions.Password = "bhtyjrr6AzDh";
sessionOptions.PortNumber = 522;
// Укажем протокол явно, это хорошая практика
sessionOptions.Protocol = 1; // 0=SFTP (по-умолчанию в старых версиях), 1=SCP, 2=FTP, 3=SFTP
// Уточните протокол у администратора удаленного сервера. Для SFTP нужно значение 3.
// Либо sessionOptions.Protocol = Протокол.Sftp, если есть доступ к перечислению WinSCP.
// Строку с динамическим сканированием убираем
// fingerprint = session.ScanFingerprint(sessionOptions,"SHA-256");
// Вместо этого жестко прописываем заранее известный отпечаток
// Важно! Укажите полный формат, который показывает WinSCP, включая "sha256;"
sessionOptions.SshHostKeyFingerprint = "sha256;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=";
session.Open(sessionOptions);
Этот способ позволяет полностью обойти проблемное место и сделать код более предсказуемым и безопасным.
Если предыдущие методы не помогли или вы хотите докопаться до истинной причины, можно включить детальное логирование сессии самого WinSCP. COM-объект позволяет указать путь к файлу, куда будут записываться все шаги подключения.
Как включить логирование:
Добавьте в код строку для установки свойства SessionLogPath перед вызовом метода Open или ScanFingerprint.
Попытка
session = Новый COMОбъект("WinSCP.Session");
// Включаем логирование
// Убедитесь, что у пользователя службы сервера 1С есть права на запись в этот каталог!
session.SessionLogPath = "C:\1C_Logs\winscp_log.txt";
sessionOptions = Новый COMОбъект("WinSCP.SessionOptions");
// ... дальнейшие настройки
fingerprint = session.ScanFingerprint(sessionOptions,"SHA-256");
// ...
Исключение
// ...
КонецПопытки;
После того как регламентное задание отработает с ошибкой, откройте файл C:\1C_Logs\winscp_log.txt. В нем вы найдете подробный отчет о попытке подключения. Скорее всего, последние строки перед ошибкой явно укажут на причину: Network error: Connection timed out (проблема с сетью/брандмауэром), Access denied (проблема с правами) или что-то еще, что поможет точно диагностировать и устранить неисправность.