Подводные камни сетевых дисков и UNC-путей ── как бизнес-приложения работают с файловым сервером (общей папкой)
· Го Комура · Сетевой диск, UNC-путь, SMB, Общий доступ к файлам, Служба Windows, Файловый сервер, C#, .NET, Сеть, Расследование сбоев, Разработка Windows, Техническая консультация
«На машине разработчика всё работало, а у заказчика выдаёт „Z:\ не найден“». «Стоило перенести задачу в Планировщик заданий - и вывод в общую папку начал падать». «Как только сделали Windows-службу, файловый сервер перестал быть виден». В консультациях по бизнес-приложениям проблемы, связанные с файловым сервером (общей папкой), - это едва ли не самая типичная категория трудностей. Приём данных заказов, вывод отчётов и CSV, наблюдение за файлами, которые выкладывает оборудование, - в локальных (on-premises) бизнес-системах общая папка и сегодня остаётся вполне рабочим инструментом интеграции.
Неприятность в том, что многие проблемы такого рода - это не «баг в коде», а следствие устройства самой Windows: связи между буквой диска и сеансом входа, учётной записи запуска службы и аутентификации, управления соединениями SMB. Отладчик здесь ни к чему не приведёт, а фраза «на моей машине не воспроизводится» будет съедать время впустую.
В этой статье, ориентируясь на разработчиков бизнес-приложений (WinForms/WPF/службы Windows), которым нужно реализовать вывод, приём или наблюдение за файлами в общей папке, мы разберём подводные камни сетевых дисков и UNC-путей на уровне устройства системы и соберём практические правила в виде таблицы решений.
1. Сначала вывод
- Буква диска (Z: и подобные) - это не общесистемный ресурс, а ресурс, привязанный к сеансу входа в систему. Каждому сеансу входа выделяется собственный полный набор букв дисков от A до Z, поэтому диск, подключённый пользователем, невидим для процесса, работающего под другим пользователем, и для службы, работающей в другом сеансе входа.1
- Если для администратора включён UAC, создаются два сеанса входа - обычный и повышенный, - и подключения дисков (символические ссылки DosDevices) независимы для каждого сеанса. Именно поэтому возникает ситуация «Z: пропадает только в приложении, запущенном с повышением прав». Существует обходной путь через параметр реестра
EnableLinkedConnections, который делает подключение общим для обоих сеансов, но Microsoft прямо указывает, что это неподдерживаемый параметр, который «может снизить безопасность системы».23 - Когда служба (или любой процесс, работающий в другом контексте безопасности) обращается к удалённому ресурсу, официальная рекомендация - использовать UNC-путь (
\\server\share\...). Подключение буквы диска изнутри службы черезnet useили функции WNet не рекомендуется - из-за риска утечки учётных данных и взаимного влияния служб друг на друга.1 - То, кому нужно выдать права на стороне общего ресурса, определяется учётной записью запуска службы. LocalSystem и NetworkService в сети аутентифицируются как «собственные учётные данные компьютера», а LocalService предъявляет анонимные учётные данные и непригодна для доступа к общим ресурсам. Служба, работающая под локальной учётной записью пользователя, вообще не может обращаться к сетевым ресурсам. На практике реальный выбор - это доменная учётная запись или gMSA, позволяющая переложить управление паролем на ОС.45678
- Нельзя одновременно удерживать соединения с одним и тем же сервером под двумя разными наборами учётных данных. Второе соединение завершится ошибкой 1219 (
ERROR_SESSION_CREDENTIAL_CONFLICT), и это поведение заложено намеренно (by design).910 - «Медленно, обрывается, иногда недоступен» - нормальное поведение общей папки, а не исключение. Простаивающее соединение по умолчанию отключается сервером через 15 минут (и незаметно восстанавливается при следующем обращении).
File.Existsпри нехватке прав или иной ошибке возвращаетfalse, не выбрасывая исключение, поэтому не позволяет отличить «файла нет» от «сервер недоступен».1112 - FileSystemWatcher поддерживает наблюдение за сетевым диском и удалённым компьютером, но проектировать нужно с расчётом на потерю событий. Переполнение буфера может привести к потере событий, а при наблюдении по сети верхний предел внутреннего буфера ограничен 64 КБ. Совмещение с опросом (полным сканированием) - стандартная практика.13
2. Как связаны буква диска и UNC-путь ── Z: принадлежит «вашему сеансу входа»
Когда в Проводнике подключаешь \\fileserver\share к Z:, кажется, будто на всей машине появился «диск Z:». Это первое заблуждение.
Официальная документация формулирует это однозначно. Буква диска - не глобальный для системы ресурс: каждому сеансу входа выделяется собственный набор от A до Z. Перенаправленный диск (сетевой диск) нельзя разделить между процессами, работающими под разными учётными записями, а служба, работающая в другом сеансе входа, не может обратиться к букве диска, установленной в другом сеансе.1 Система управляет подключениями дисков на основе logon SID, уникально идентифицирующего сеанс входа.1
Иными словами, Z: - это просто «сокращённое обозначение пути, по одному набору на сеанс входа», а физически за ним всегда стоит UNC-путь. Отсюда цепочкой объясняется целый ряд симптомов, часто встречающихся на практике.
| Симптом | Причина на уровне устройства |
|---|---|
| При запуске под другим пользователем Z: отсутствует | Буква диска привязана к сеансу входа; чужое подключение не видно1 |
| При запуске «от имени администратора» Z: отсутствует | UAC создаёт два сеанса входа - обычный и повышенный, - и подключения между ними не разделяются2 |
| После переноса в Планировщик заданий/службу Z: отсутствует | Выполнение идёт в другом сеансе входа. Даже если служба настроена на учётную запись пользователя, система всё равно создаёт для неё новый сеанс входа1 |
Историю с UAC стоит разобрать чуть подробнее. Когда пользователь из группы администраторов входит в систему, система создаёт два связанных сеанса входа: один с ограниченным токеном, другой - с полным токеном администратора. Физическая сущность подключения диска - объект символической ссылки (DosDevices), связывающий букву диска с UNC-путём; этот объект принадлежит конкретному сеансу входа и не разделяется между сеансами.2 Сценарии входа выполняются на стороне обычных прав, поэтому из процесса с повышенными правами это подключение просто не видно - вот и вся логика.
Если установить EnableLinkedConnections (значение DWORD в разделе HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System) равным 1, символическая ссылка записывается сразу в оба связанных сеанса, и этот симптом исчезает. Однако официальная документация прямо предупреждает: «этот обходной путь может снизить безопасность системы. Microsoft не поддерживает этот обходной путь, он предоставляется „как есть“».3 Закладывать в окружение заказчика неподдерживаемую настройку реестра - плохое решение для бизнес-приложения. Правильный путь - писать приложение с расчётом на UNC-пути. В официальном руководстве по устранению неполадок перенаправления папок тоже рекомендуется «всегда использовать UNC-пути вместо буквы подключённого диска».3
Реализация при этом простая: достаточно хранить пути в конфигурации в виде UNC.
// appsettings.json ── храним пути в виде UNC, а не буквы диска
{
"FileTransfer": {
"IncomingDir": "\\\\fileserver01\\edi\\incoming",
"ProcessedDir": "\\\\fileserver01\\edi\\processed"
}
}
Если приложение позволяет пользователю выбрать путь под Z: через диалог выбора папки, нормализация в UNC при сохранении убережёт от поломки, когда позже сменится контекст выполнения. Для преобразования пути с буквой диска в UNC предусмотрена функция WNetGetUniversalName.14
3. Доступ к общей папке из службы Windows ── UNC обязателен, и «от чьего имени» идёт доступ
Как показано в предыдущей главе, подключённый диск из службы недоступен. Официальная документация указывает, что служба (и любой процесс, работающий в другом контексте безопасности) должна обращаться к удалённым ресурсам по UNC-имени, и прямо не рекомендует реализацию, при которой служба во время выполнения подключает букву диска через net use или функции WNet. Причины, которые там названы: подключение становится видимым другим службам, работающим в том же контексте, учётные данные, переданные в net use, могут утечь за границы службы, а несколько служб, пытающихся установить одно и то же подключение, начинают мешать друг другу ошибкой «уже подключено».1
Как только путь стал UNC, следующий вопрос - аутентификация. От чьего имени служба обращается к файловому серверу? Это определяется учётной записью запуска, и именно она определяет, кому нужно выдать права на стороне общего ресурса.
| Учётная запись запуска | Сетевая идентичность | Выдача прав на стороне общего ресурса | Вывод |
|---|---|---|---|
| LocalSystem | Собственные учётные данные компьютера4 | В доменной среде - права общего доступа и NTFS учётной записи компьютера (DOMAIN\MACHINE$) | Работает, но с избыточными правами. При замене машины права нужно настраивать заново |
| NetworkService | Собственные учётные данные компьютера5 | То же самое | Минимальные локальные права, но та же сетевая идентичность, что и у LocalSystem |
| LocalService | Анонимные учётные данные6 | Выдать нечего | Непригодна для доступа к общим ресурсам |
| Локальный пользователь | - | - | Не может обращаться к сетевым ресурсам7 |
| Доменный пользователь | Эта учётная запись | Права выдаются этой учётной записи | Практический стандарт. Проблемное место - эксплуатация смены пароля |
| gMSA | Эта учётная запись | Права выдаются этой учётной записи | Пароль автоматически управляется ОС (автоматическая смена каждые 30 дней). Лучший вариант там, где поддерживается815 |
То, что LocalSystem и NetworkService выходят в сеть «от имени компьютера», - официально задокументированный факт.45 В документации по BITS прямо изложено практическое следствие: если ACL исходного файла ограничивает доступ учётной записью пользователя, служба (аутентифицирующаяся учётными данными компьютера) получит отказ в доступе, а системным учётным записям вообще не следует использовать подключённые диски.16
Отсюда вытекают три практических правила:
- При первичной диагностике проблемы «после перевода в службу доступ пропал» нужно первым делом проверить учётную запись запуска. То, что работало на рабочем столе под «вашими» правами, в службе проверяется уже под личностью из таблицы выше. Права общего доступа и ACL NTFS нужно проверять именно для этой личности.
- Для долгосрочной эксплуатации в доменной среде учётной записью запуска должна быть доменная учётная запись или gMSA. У gMSA пароль - случайные 240 байт, автоматически меняемые ОС каждые 30 дней, что структурно устраняет инциденты вроде «в понедельник утром всё встало из-за истёкшего пароля учётной записи службы».15
- В рабочей группе (без домена) аутентификация учётными данными компьютера не работает, поэтому в дело вступают явные учётные данные, о которых пойдёт речь в следующей главе.
О том, как строить саму службу (настройка учётной записи запуска, параметры восстановления, безопасная остановка), мы рассказываем в статье «Как создавать и эксплуатировать службу Windows», а об устройстве сеансов и входа в систему - в статье «Как понимать изоляцию сеансов Windows».
4. Работа с учётными данными ── net use, диспетчер учётных данных и ошибка 1219
Если самой учётной записи запуска нельзя выдать права на стороне общего ресурса (рабочая группа, NAS с собственной системой учётных записей и т. п.), приходится подключаться, явно передавая учётные данные. Основных способов три:
net use \\server\share /user:...── устанавливает подключение для данного сеанса входа. Удобно для интерактивной проверки, но, как уже говорилось, выполнение изнутри службы не рекомендуется.1- Диспетчер учётных данных (
cmdkey) ── командаcmdkey /add:server /user:svc-file /pass:...сохраняет учётные данные, и в дальнейшем при аутентификации к этому серверу они подставляются автоматически.1718 Ловушка в том, что хранилище привязано к профилю пользователя, поэтому для использования в службе регистрировать данные нужно именно в контексте учётной записи запуска службы. - Подключение из кода (
WNetAddConnection2) ── позволяет установить подключение к сетевому ресурсу, явно указав учётные данные клиента. Официальная документация тоже упоминает этот способ как одну из стратегий доступа серверного процесса к сетевым ресурсам.19 Если сделать «подключение без устройства» (deviceless), не назначая буквы диска, доступ остаётся возможным по UNC-пути.
И самая известная ловушка в этой области - ошибка 1219.
Multiple connections to a server or shared resource by the same user, using more than one user name, are not allowed. (ERROR_SESSION_CREDENTIAL_CONFLICT, 1219)10
Нельзя из одного и того же сеанса входа установить несколько подключений к одному серверу под разными именами пользователей. Если попытаться подключиться к одному и тому же файловому серверу двумя видами учётных данных - скажем, к отделу продаж под своей учётной записью, а к общему ресурсу для интеграции систем под выделенной учётной записью, - второе подключение упадёт с ошибкой 1219. Официальная документация прямо называет это поведением «by design» (заложенным намеренно), а перечисленные обходные пути - «подключаться по IP-адресу» или «создать отдельный псевдоним DNS», то есть сделать вид, что это другой сервер.9
На практике первым выбором должно быть избегание самой схемы с несколькими наборами учётных данных для одного сервера: свести всё к одной учётной записи для интеграции и выдать этой учётной записи права на все нужные общие ресурсы. Только если это невозможно по каким-то причинам, стоит разделять пути через псевдоним (alias).
Однако псевдоним - это не просто «добавить одну запись CNAME в DNS, и готово». В среде с Kerberos-аутентификацией SMB-клиент пытается аутентифицироваться по SPN (имени субъекта-службы), соответствующему имени цели подключения, поэтому если для псевдонима не зарегистрирован SPN, обращение через CNAME может провалиться уже на этом этапе. Официальное руководство по устранению неполадок тоже называет отсутствие SPN одной из причин и рекомендует настраивать псевдоним не через CNAME в DNS, а как псевдоним имени компьютера командой netdom computername <имя_сервера> /add:<псевдоним>.20 Прежде чем закладывать подключение через псевдоним в архитектуру как способ обхода ошибки 1219, обязательно проверьте его работоспособность в целевом окружении.
Ещё один момент: продвинутое требование «пусть служба обращается к файловому серверу с правами того клиентского пользователя, который к ней подключился» - это область не переиспользования учётных данных, а impersonation. Официальная документация тоже рекомендует именно impersonation вместо того, чтобы служба держала собственные учётные данные.1 О правильной реализации impersonation и о дополнительных нюансах для удалённых ресурсов рассказано в статье «Как правильно работать с токенами impersonation в Windows».
5. Проектируем с расчётом на «медленно, обрывается, иногда недоступен»
Код, написанный с тем же настроем, что и для локального диска, рано или поздно обязательно сломается на общей папке. Есть три реальности, которые нужно закладывать заранее.
Во-первых, разрыв соединения - это нормально. Простаивающее соединение по умолчанию отключается через 15 минут таймаута - чтобы не тратить впустую ресурсы сервера. Это тот самый знакомый красный крестик на значке сетевого диска в Проводнике, а при следующем обращении подключение оперативно восстанавливается.11 Иными словами, и то, что бизнес-приложение, обращающееся к папке нечасто, при каждом обращении на мгновение подвисает, и то, что инструмент мониторинга «кричит»: «отключено!» - хотя реального вреда нет, - оба этих явления являются штатным поведением системы. Судить нужно не по наличию подключения, а по тому, успешно ли проходит реальная операция ввода-вывода.
Во-вторых, ошибки сообщаются неинформативно. File.Exists возвращает false без исключения независимо от того, некорректен ли путь, не хватает ли прав или произошёл сбой диска.12 Локально «false значит файла нет» почти никогда не создаёт проблем, но на общей папке в одно и то же false схлопываются и «файла нет», и «сервер недоступен / нет прав», поэтому код, принимающий бизнес-решение на основе Exists, при сетевом сбое тихо завершается успешно как «объект не найден» - неприятный сценарий поломки. Более пригодная для диагностики на общей папке схема - открывать файл напрямую, без предварительной проверки существования, и различать случаи по типу исключения.
В-третьих, ресурс на другой стороне может ещё отсутствовать. Сразу после загрузки машины запуск службы может опередить готовность сети, да и сам файловый сервер может быть в процессе перезапуска. Персистентное подключение (persistent connection) - это механизм, восстанавливаемый при входе пользователя в систему,21 поэтому в мире службы, которая никогда не проходит через вход в систему, нет гарантии, что «после запуска общий ресурс сразу виден». Правильное поведение - не проверить связь один раз при старте и завершиться при неудаче, а повторять попытки, ожидая готовности.
Если заложить все три момента, каркас стороны записи выглядит так:
// Временные сетевые ошибки повторяем, бизнес-ошибки завершаем немедленно
private static async Task WriteToShareAsync(string finalPath, byte[] content, CancellationToken ct)
{
var dir = Path.GetDirectoryName(finalPath)!;
var tempPath = Path.Combine(dir, $"~{Guid.NewGuid():N}.tmp");
try
{
for (var attempt = 1; ; attempt++)
{
try
{
await File.WriteAllBytesAsync(tempPath, content, ct);
File.Move(tempPath, finalPath); // rename в той же директории публикует "завершение"
return;
}
catch (IOException ex) when (attempt < 5 && IsRetryable(ex))
{
// Повторяем только временные сетевые сбои с экспоненциальной задержкой (с ограничением попыток)
_logger.LogWarning(ex, "Не удалось записать в общий ресурс (попытка {Attempt}). Повторяем", attempt);
await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, attempt)), ct);
}
}
}
catch
{
// Если сдаёмся и пробрасываем исключение дальше, best-effort чистим временный файл
try { File.Delete(tempPath); } catch { /* Ошибку очистки игнорируем */ }
throw;
}
}
// IOException прилетает с одним и тем же типом и при "разрыве сети", и при "файле с тем же
// именем в месте назначения", и при "переполненном диске". По младшим 16 битам HResult
// (код ошибки Win32) отбираем только те временные сбои, для которых повтор имеет смысл
private static bool IsRetryable(IOException ex)
{
var win32 = ex.HResult & 0xFFFF;
return win32 is 53 // ERROR_BAD_NETPATH: сетевой путь не найден
or 59 // ERROR_UNEXP_NET_ERR: непредвиденная сетевая ошибка
or 64 // ERROR_NETNAME_DELETED: сетевое имя больше не доступно
or 121; // ERROR_SEM_TIMEOUT: истёк тайм-аут (так называемый semaphore timeout)
}
Важно и не писать небрежно «раз это IOException - повторяем». Ошибки, результат которых не изменится сколько ни повторяй - файл с тем же именем уже есть в месте назначения, путь слишком длинный, диск переполнен, - прилетают точно тем же типом IOException, поэтому судить только по типу исключения означает пять раз повторять постоянную ошибку, впустую выжидая экспоненциальную задержку. Как в примере выше, отбирайте по коду ошибки Win32 (53/59/64/121 и подобные коды разрыва/тайм-аута для доступа к общему ресурсу22) только те сбои, «для которых повтор действительно имеет смысл». Реалистичный подход - постепенно дополнять этот список кодами, которые реально наблюдаются в логах боевого окружения.
Суть не столько в самом добавлении повторов, сколько в том, чтобы сочетать повторы с протоколом передачи, безопасным для повторного выполнения (temp -> rename, идемпотентность). Если запись прервётся на середине, останется недописанный файл. Если договориться, что конечное имя появляется только через rename после закрытия файла, это защитит принимающую сторону от чтения «сырого» файла. Учтите также, что при аварийном завершении процесса или отключении питания код очистки внутри функции просто не выполнится, поэтому стоит предусмотреть и периодическую очистку папки обмена - «удалять файлы ~*.tmp, которые давно не обновлялись», - чтобы мусор не накапливался и не забивал папку. Полная картина этой схемы передачи собрана в статье «Основы блокировок для файловой интеграции».
Есть ещё одна особенность, характерная именно для сети. Результат «неудача» не гарантирует, что неудача действительно произошла. Если соединение обрывается сразу после того, как на стороне сервера завершился rename, клиент может получить исключение, хотя файл под конечным именем уже опубликован. Если в этом состоянии бездумно повторить попытку, можно либо упереться в ошибку «файл с таким именем уже есть в месте назначения», либо - если принимающая сторона уже забрала первый файл - опубликовать те же данные повторно. Решение - перед повтором неудавшегося шага rename проверить состояние места назначения и сверить его. Если включить в конечное имя идентификатор обработки (номер накладной или GUID выполнения), можно распознать ситуацию «конечный файл с этим же ID уже существует» как признак того, что предыдущий rename на самом деле прошёл успешно, и выйти, засчитав операцию как успешную; а принимающая сторона по тому же ID сможет отбросить повторный приём.
6. Блокировки через SMB и надёжность FileSystemWatcher
6.1. Не стоит слишком полагаться на блокировки
К общей папке одновременно обращается несколько клиентов. Открытие с FileShare.None действительно даёт эксклюзивность, пока дескриптор открыт, даже через SMB, но проектирование, опирающееся на «если блокировка получена - значит безопасно», рушится, когда дескриптор теряется из-за разрыва соединения или когда в дело вмешивается участник, не берущий блокировку (ручное копирование, другая система). Настоящую основу взаимного исключения стоит строить не на блокировке ОС, а на самом протоколе передачи: temp -> rename, атомарный claim (обрабатывает только тот, кто выиграл rename из incoming в processing) и идемпотентность. Подробности этого подхода - в статье о блокировках, упомянутой выше.
6.2. Реальность использования FileSystemWatcher на удалённом общем ресурсе
FileSystemWatcher официально документирован как поддерживающий наблюдение за файлами не только локально, но и на сетевых дисках и удалённых компьютерах.13 Само по себе его использование оправдано. Однако есть два предостережения о надёжности.
- Уведомления приходят через буфер, и переполнение приводит к их потере. Если изменения происходят кучно за короткое время, события сверх размера буфера теряются.13
- При наблюдении по сети верхний предел
InternalBufferSizeограничен 64 КБ. Это ограничение «нельзя раздуть больше», в отличие от локального наблюдения, зафиксировано явно.13
К тому же в практике расследования сбоев я неоднократно видел ситуацию, когда при перезапуске или разрыве соединения с сервером на другой стороне наблюдатель просто молча умирает, и никакого уведомления об этом не приходит вовсе. Практический вывод - относиться к FileSystemWatcher на удалённом общем ресурсе как к „подсказке для более быстрой реакции“, а источником истины считать полное сканирование (опрос) при запуске, при ошибке и по расписанию. Схемы проектирования, включая обработку дублирующихся и приходящих не по порядку событий, подробно разобраны в статье «Практическое руководство по FileSystemWatcher».
7. Практические правила (таблица решений)
Сведём всё изложенное выше в таблицу решений по спорным вопросам проектирования.
| Вопрос | Варианты | Ориентир для решения |
|---|---|---|
| Как хранить пути | Буква диска / UNC-путь | Путь, с которым работает приложение, всегда должен быть UNC. Букву диска стоит считать чисто удобством пользователя на экране1 |
| Запись в общий ресурс | Прямая запись / temp -> rename | Прямая запись не даёт гарантии «незавершённый файл не будет прочитан». Основа - договорённость, что конечное имя означает завершение |
| Обнаружение новых файлов | FileSystemWatcher отдельно / опрос / сочетание | На удалённом общем ресурсе считайте, что события теряются. Если приемлема задержка в несколько минут - опрос сам по себе проще и надёжнее; если нужна немедленность - сочетайте оба способа13 |
| Учётная запись запуска службы | LocalSystem / доменная учётная запись / gMSA | При доступе к общему ресурсу - доменная учётная запись или gMSA. Там, где gMSA доступна, можно вообще убрать эксплуатацию пароля8 |
| Когда нужны отдельные учётные данные | Вызов net use из кода / WNetAddConnection2 / предварительная регистрация через cmdkey | net use изнутри службы не рекомендуется. Несколько наборов учётных данных к одному серверу упираются в ошибку 1219, поэтому обходите это ещё на этапе проектирования - через объединение учётных записей или псевдоним DNS19 |
| Прямой доступ множества клиентов | Каждое устройство напрямую по UNC / через промежуточную службу (API) | Когда произведение числа устройств, наборов учётных данных и одновременных обращений становится неуправляемым, стоит свести обращения к общему ресурсу в одну службу, а клиентам общаться с ней через API |
Последняя строка требует пояснения. Интеграция через общую папку удобна, но чем больше точек доступа, тем экспоненциально сложнее становится управление тем, «у кого какие права» и «кто с кем конфликтует». После определённого масштаба стоит свести процесс, обращающийся к файловому серверу, в единую службу Windows, а каждому клиенту общаться с ней по HTTP или gRPC. Понижение статуса общей папки с «интерфейса между системами» до «внутренней реализации этой одной службы» в долгосрочной перспективе оказывается наиболее удобной в сопровождении формой.
8. Итог
- Буква диска - лишь символ, привязанный к сеансу входа. То, что она невидима для другого пользователя, повышенного процесса или службы, заложено намеренно, и правильный путь - писать приложение с расчётом на UNC-пути.
EnableLinkedConnections- неподдерживаемый обходной путь. - Доступ к общему ресурсу из службы требует UNC. От чьего имени идёт аутентификация, определяется учётной записью запуска: LocalSystem/NetworkService используют учётные данные компьютера, LocalService - анонимные. На практике права на стороне общего ресурса выдаются доменной учётной записи или gMSA.
- Одновременные подключения к одному серверу под разными учётными данными завершаются ошибкой 1219 (заложено намеренно). Избегайте этого ещё на этапе проектирования - через объединение учётных записей или псевдоним DNS.
- «Медленно, обрывается, иногда недоступен» - нормальное поведение общей папки. Отключение по простою (по умолчанию 15 минут) - не аномалия, а
falseотFile.Existsнеотличим от сетевого сбоя. Закладывайте повторы, temp -> rename и идемпотентность вместе. - FileSystemWatcher поддерживает удалённое наблюдение, но из-за переполнения буфера и предела в 64 КБ события теряются, поэтому сочетание с полным сканированием - стандартная практика.
- Если сомневаетесь - обращайтесь к таблице решений из главы 7. А когда масштаб вырастет, рассмотрите вариант со сведением доступа к общему ресурсу в промежуточную службу.
Похожие статьи
- Основы блокировок для файловой интеграции - лучшие практики файловых блокировок и атомарного claim
- Практическое руководство по FileSystemWatcher - защита от потерь и дублирования событий
- Как создавать и эксплуатировать службу Windows ── от выбора между Планировщиком заданий и BackgroundService до превращения в службу
- Как понимать изоляцию сеансов Windows ── Session 0, RDP и одновременная работа нескольких пользователей
- Как правильно работать с токенами impersonation в Windows ── заимствование прав на уровне потока и безопасный возврат
Смежные направления консультаций
В KomuraSoft LLC мы занимаемся проектированием и реализацией систем файловой интеграции через общие папки, прорабатываем настройку прав доступа и аутентификации при переводе приложения в службу Windows, а также расследуем сбои вроде «после перевода в службу общий ресурс перестал быть виден» или «падает только в конкретном окружении».
- Разработка Windows-приложений
- Расследование сбоев и анализ первопричин
- Техническая консультация и ревью проекта
- Контакты
Справочные ссылки
-
Microsoft Learn, Services and Redirected Drives. О том, что буквы дисков назначаются не глобально для системы, а по сеансу входа, о том, что службы не могут обращаться к буквам дисков другого сеанса и должны использовать UNC-имена, о причинах, по которым не рекомендуется подключение дисков изнутри службы через net use или функции WNet (утечка учётных данных, взаимное влияние служб и т. п.), о том, что для службы, настроенной под учётную запись пользователя, всё равно создаётся новый сеанс входа, и о том, что вместо удержания собственных учётных данных службой рекомендуется impersonation клиента. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 ↩11
-
Microsoft Learn, Mapped drives are not available from an elevated prompt when UAC is configured to Prompt for credentials. О двух связанных сеансах входа, создаваемых при включённом UAC, о том, что подключение диска физически представляет собой объект символической ссылки (DosDevices), привязанный к конкретному сеансу входа и не разделяемый между сеансами, и о том, что EnableLinkedConnections принудительно записывает символическую ссылку в оба сеанса. ↩ ↩2 ↩3
-
Microsoft Learn, Folder Redirection fails to apply when redirected to mapped drive letter, instead of UNC path. О том, что при входе администратора LSA создаёт два токена доступа, а диск подключается под стандартным токеном, о порядке настройки параметра реестра EnableLinkedConnections вместе с предупреждением «этот обходной путь может снизить безопасность системы, Microsoft его не поддерживает», и о том, что всегда рекомендуется использовать UNC-пути вместо буквы диска. ↩ ↩2 ↩3
-
Microsoft Learn, LocalSystem Account. О том, что LocalSystem обладает широкими локальными привилегиями, но в сети предъявляет удалённым серверам собственные учётные данные компьютера. ↩ ↩2 ↩3
-
Microsoft Learn, NetworkService Account. О том, что NetworkService обладает минимальными локальными привилегиями, но в сети предъявляет удалённым серверам собственные учётные данные компьютера. ↩ ↩2 ↩3
-
Microsoft Learn, LocalService Account. О том, что LocalService предъявляет в сети анонимные учётные данные. ↩ ↩2
-
Microsoft Learn, About Service Logon Accounts. О том, что учётная запись входа службы определяет её контекст безопасности во время выполнения, и о том, что служба, работающая в контексте безопасности локальной учётной записи пользователя, не может обращаться к сетевым ресурсам. ↩ ↩2
-
Microsoft Learn, Group Managed Service Accounts overview. О том, что gMSA - это доменная учётная запись, обеспечивающая автоматическое управление паролем и упрощённое управление SPN, что позволяет переложить управление паролем на саму ОС Windows. ↩ ↩2 ↩3
-
Microsoft Learn, The network folder specified is currently mapped using a different user name and password error. О том, что ошибка при попытке установить несколько подключений к одному серверу с разными учётными данными заложена намеренно (by design), и о том, что обходными путями являются подключение по IP-адресу или создание отдельного псевдонима DNS. ↩ ↩2 ↩3
-
Microsoft Learn, System Error Codes (1000-1299). Об определении и тексте сообщения ошибки 1219 (ERROR_SESSION_CREDENTIAL_CONFLICT). ↩ ↩2
-
Microsoft Learn, Mapped drive connection to network share may be lost. О том, что простаивающее соединение по умолчанию отключается после 15-минутного тайм-аута (autodisconnect), и о том, что значок диска в Проводнике при этом отмечается красным крестиком, но при обращении соединение оперативно восстанавливается. ↩ ↩2
-
Microsoft Learn, File.Exists(String) Method. О том, что метод возвращает false без исключения при отсутствии прав на чтение, а также возвращает false при возникновении любой ошибки во время проверки существования (неверный путь, сбой диска, нехватка прав и т. п.). ↩ ↩2
-
Microsoft Learn, FileSystemWatcher Class. О поддержке наблюдения за файлами на локальном компьютере, сетевом диске и удалённом компьютере, о возможной потере событий при превышении размера буфера, и о том, что при наблюдении по сети максимальное значение InternalBufferSize составляет 64 КБ. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, WNet Functions. О том, что WNetGetUniversalName - это функция получения универсального (UNC) имени из пути с буквой диска. ↩
-
Microsoft Learn, Secure group managed service accounts. О том, что пароль gMSA представляет собой случайно сгенерированные 240 байт, автоматически меняемые ОС каждые 30 дней, что избавляет администраторов от необходимости планировать смену пароля и простой службы. ↩ ↩2
-
Microsoft Learn, Service Accounts and BITS. О том, что сетевая аутентификация LocalSystem/NetworkService проходит по учётным данным компьютера, а LocalService - по анонимным учётным данным, о том, что при ограничении ACL учётной записью пользователя доступ будет отклонён, и о том, что системным учётным записям не следует использовать подключённые диски. ↩
-
Microsoft Learn, cmdkey. О том, что команда cmdkey позволяет создавать, перечислять и удалять сохранённые имена пользователей и пароли (учётные данные). ↩
-
Microsoft Learn, Credentials processes in Windows authentication. О том, что диспетчер учётных данных сохраняет учётные данные в контейнере учётных данных Windows и автоматически предъявляет их при последующей аутентификации. ↩
-
Microsoft Learn, Client Access to Network Resources. О том, что установление подключения через WNetAddConnection2 с указанием учётных данных клиента упоминается как одна из стратегий доступа серверного процесса к сетевым ресурсам. ↩
-
Microsoft Learn, SMB file server share access is unsuccessful through DNS CNAME alias. О том, что одной из причин сбоя доступа по SMB через CNAME является отсутствие регистрации SPN для псевдонима, и о рекомендации определять псевдоним командой netdom computername, а не через CNAME в DNS. ↩
-
Microsoft Learn, Windows Networking Operations. О том, что персистентное подключение (persistent connection) - это сетевое подключение, автоматически восстанавливаемое системой при входе пользователя в систему. ↩
-
Microsoft Learn, System Error Codes (0-499). Об определениях ERROR_BAD_NETPATH (53), ERROR_UNEXP_NET_ERR (59), ERROR_NETNAME_DELETED (64) и ERROR_SEM_TIMEOUT (121). ↩
Похожие статьи
Недавние статьи с теми же тегами помогут подробнее изучить близкие темы.
Спящий режим, гибернация, Modern Standby и долго работающие приложения — как проектированием предотвратить «остановилось ночью»
Разбираем, почему долго работающее Windows-приложение оказывается «остановленным к утру», начиная с различий между спящим режимом S3, гиб...
MAX_PATH и подводные камни путей/имён файлов в Windows — лимит 260 символов, зарезервированные имена, конечная точка, регистр
Разбираем ограничения путей и имён файлов, которые часто стоят за классической ошибкой «файл не найден». Рассматриваем состав лимита MAX_...
Если ваше Windows-приложение приняли за вирус — как реагировать на ложные срабатывания Microsoft Defender и жить с влиянием на производительность
Разбираем правильный порядок действий, если Microsoft Defender ложно определяет ваше Windows-приложение как вредоносное: как устроена сов...
Работают ли бизнес-приложения на Windows на Arm — реальность x64-эмуляции (Prism) и нативных DLL/COM
Отвечаем разработчикам и ИТ-специалистам на вопрос «заработает ли наше бизнес-приложение на Windows на Arm». Разбираем принцип работы x64...
Японская эра, праздники и даты закрытия периода в бизнес-приложениях — устойчивый к смене эры дизайн, JapaneseCalendar и расчёт рабочих дней на практике
Показать «Рэйва 8» в отчёте, рассчитать рабочие дни без учёта праздников, оплатить в последний рабочий день месяца, следующего за закрыти...
Связанные темы
Эти страницы показывают тему статьи в более широком контексте услуг и решений.
Технические темы Windows
Раздел о разработке Windows, расследовании сбоев и использовании существующих активов.
Услуги по этой теме
Статья напрямую связана со следующими услугами.
Разработка приложений для Windows
Бизнес-приложения, интеграция оборудования и средства связи — от требований до разработки.
Частые вопросы
Вопросы, которые часто возникают при консультациях по теме статьи.
- Почему служба Windows не может обратиться к сетевому диску (Z:)?
- Потому что буква диска - это не общесистемный ресурс, а ресурс, привязанный к конкретному сеансу входа в систему (logon session). Диск Z:, который пользователь подключил через Проводник, - это лишь символ, принадлежащий сеансу входа этого пользователя, и он невидим для службы, работающей в другом сеансе входа. Даже если служба запущена под учётной записью пользователя, система создаёт для неё новый сеанс входа, поэтому подключение диска на стороне рабочего стола не переносится на эту же учётную запись в службе. Правильный способ для службы - обращаться к ресурсу по UNC-пути \\server\share.
- Как службе, работающей под LocalSystem, получить доступ к общей папке?
- В сети LocalSystem аутентифицируется как собственные учётные данные компьютера. В доменной среде можно предоставить учётной записи компьютера (DOMAIN\MACHINE$) права на стороне общего ресурса - и в разрешениях общего доступа, и в ACL NTFS. Однако такая схема неудобна в эксплуатации: при замене машины права придётся настраивать заново. Поэтому на практике мы рекомендуем запускать службу под доменной учётной записью или gMSA (групповой управляемой учётной записью службы) и выдавать права на общий ресурс именно этой учётной записи.
- Можно ли наблюдать за файлами в общей папке с помощью FileSystemWatcher?
- Использовать можно, но полагаться только на него нельзя. FileSystemWatcher официально поддерживает наблюдение за сетевыми дисками и удалёнными компьютерами, но уведомления об изменениях приходят через внутренний буфер, и если уведомления идут потоком, буфер может переполниться и часть событий будет потеряна. Более того, при наблюдении по сети верхний предел размера буфера ограничен 64 КБ. Уведомления стоит рассматривать лишь как повод для повторного сканирования и обязательно сочетать их с полным сканированием (опросом) при запуске, при ошибке и по расписанию.
- Как сделать обмен файлами устойчивым к разрывам сети?
- Нужно проектировать так, будто «общая папка медленная, соединение рвётся, а сервер иногда недоступен» - это нормальное поведение, а не исключение. Конкретно: запись вести под временным именем файла и переименовывать его в конечное имя только после закрытия дескриптора (temp -> rename), операции чтения и записи оборачивать в повторные попытки, а временные сетевые ошибки отличать от собственно бизнес-ошибок. Важно учитывать, что File.Exists возвращает false даже тогда, когда ресурс просто недоступен, - то есть не позволяет отличить «файла нет» от «сервер не отвечает», - а последним рубежом защиты остаётся идемпотентность обработки, при которой повторная обработка не приводит к порче данных.
Об авторе
Страница с профилем автора статьи.
Го Комура
Представитель KomuraSoft LLC
Специализируется на разработке программного обеспечения для Windows, техническом консалтинге и расследовании сбоев, особенно в проектах с унаследованными системами и трудно воспроизводимыми ошибками.
Публичные ссылки