АйТи Фреш
Главная / Статьи / Windows и рабочие места
Windows и рабочие места

Windows Server 2025 просит пароль вместо SSH-ключа: разбираем administrators_authorized_keys

Автор: Семёнов Евгений Сергеевич, директор ООО «АйТи-Фреш» · · ~20 мин чтения
Windows Server 2025 просит пароль вместо SSH-ключа: разбираем administrators_authorized_keys
Иллюстрация к статье «Windows Server 2025 просит пароль вместо SSH-ключа: разбираем administrators_authorized_keys».

Сценарий знакомый: публичный ключ лежит в `C:\Users\Administrator\.ssh\authorized_keys`, клиент исправно предлагает его серверу, но Windows Server 2025 всё равно запрашивает пароль. Автоматическая выгрузка по SFTP останавливается, а администратор начинает менять алгоритмы ключей и права наугад. Я покажу, как определить фактический `AuthorizedKeysFile`, безопасно настроить ACL, не испортить файл кодировкой Windows PowerShell 5.1 и получить точную причину отказа из журнала OpenSSH.

Почему администраторский ключ ищут в ProgramData

В стандартной конфигурации OpenSSH для Windows используются два расположения ключей. Для обычной локальной или доменной учётной записи относительный путь .ssh/authorized_keys вычисляется от домашнего каталога пользователя. Например, для пользователя ivan это обычно C:\Users\ivan\.ssh\authorized_keys. Для учётной записи, которую правило Match Group administrators распознаёт как члена локальной группы администраторов, путь переопределяется на %ProgramData%\ssh\administrators_authorized_keys, обычно C:\ProgramData\ssh\administrators_authorized_keys.

Это поведение задаёт конфигурация, а не само имя учётной записи Administrator. Если доменная учётная запись получает локальные административные права через группу, она также может попасть под Match Group administrators. На доменных серверах я не полагаюсь только на список групп в оснастке: проверяю фактическое сопоставление по конфигурации и отладочному журналу, потому что вложенность групп, кэш членства и локализация имён способны усложнить диагностику.

Есть важная особенность общего файла: строки в administrators_authorized_keys не привязаны к комментариям в конце ключей. Комментарий вроде backup@server нужен человеку для инвентаризации, но не ограничивает имя входящего пользователя. Если один публичный ключ находится в общем файле, его владелец потенциально может предъявить этот ключ при входе под любой административной учётной записью, к которой применяется тот же путь. Поэтому общий файл нельзя превращать в бесконтрольную свалку ключей.

Типичная ошибка выглядит так: ключ копируют в профиль администратора, затем включают PubkeyAuthentication yes, меняют Ed25519 на RSA и очищают клиентский known_hosts. Эти действия не помогают, если сервер ищет ключ в другом файле. known_hosts вообще проверяет ключ узла и защищает клиента от подмены сервера; к выбору серверного authorized_keys он отношения не имеет.

Сначала определите, применяется ли `Match Group administrators`. Исправление ACL профильного `authorized_keys` бесполезно, если фактический путь переопределён на файл в ProgramData.

Смотрим действующий sshd_config и проверяем синтаксис

OpenSSH Server читает %ProgramData%\ssh\sshd_config. Если этого файла нет, служба создаёт его при запуске. В актуальном шаблоне Microsoft активны профильный AuthorizedKeysFile, подсистема SFTP и Match-блок, который переопределяет файл для администраторов. Но установленный сервер мог пережить обновление, перенос конфигурации, применение групповой политики или работу чужого скрипта, поэтому содержимое репозитория не заменяет проверку локального файла.

AuthorizedKeysFile .ssh/authorized_keys

Subsystem sftp sftp-server.exe

Match Group administrators
    AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys

Токен __PROGRAMDATA__ здесь является синтаксисом Windows-сборки OpenSSH, а не переменной PowerShell. Не следует механически заменять его на $env:ProgramData внутри sshd_config. Если Match-блок удалён или закомментирован, административная учётная запись использует обычное значение AuthorizedKeysFile; если блок изменён, ориентироваться нужно на указанный в нём путь.

Я проверяю нужные строки и синтаксис до перезапуска. Параметр -t переводит sshd.exe в тестовый режим, а -f указывает проверяемый конфигурационный файл. Успешная команда ничего не выводит и возвращает код завершения без ошибки. Это особенно важно после добавления Match-блоков: неудачная правка может оставить службу без рабочего конфига.

$config = Join-Path $env:ProgramData 'ssh\sshd_config'
Select-String -Path $config -Pattern '^\s*AuthorizedKeysFile|^\s*Match\s+Group|^\s*Subsystem'

& "$env:WINDIR\System32\OpenSSH\sshd.exe" -t -f $config
if ($LASTEXITCODE -ne 0) {
    throw 'sshd_config не прошёл проверку; службу не перезапускаем.'
}

Restart-Service -Name sshd

Изменение authorized_keys или его ACL проверяется при новой аутентификации и обычно не требует перезапуска службы. Изменение sshd_config, напротив, вступает в силу после перезапуска sshd. Документация Microsoft прямо требует перезапускать службу после правки конфигурации.

Не перезапускайте sshd с непроверенным конфигом на удалённом сервере. Сначала выполните тест `sshd.exe -t`, сохраняя открытую административную сессию.
Windows Server 2025 просит пароль вместо SSH-ключа: разбираем administrators_authorized_keys — схема
Схема к статье. Открыть схему в полном размере

ACL administrators_authorized_keys без опасных допусков

Для %ProgramData%\ssh\administrators_authorized_keys Microsoft требует DACL с записями только для NT AUTHORITY\SYSTEM и встроенной группы BUILTIN\Administrators. Это не совет по усилению защиты, а условие, при котором Windows OpenSSH принимает административный файл ключей. Учётная запись SYSTEM нужна потому, что от неё работает служба и читает файл; администраторам требуется возможность управлять его содержимым.

Проблема часто появляется при создании файла в Проводнике или через скрипт. Он наследует разрешения каталога, и среди записей могут оказаться Users или другие субъекты. Команда icacls.exe с /inheritance:r отключает наследование и удаляет унаследованные записи. Затем /grant добавляет полный доступ двум нужным субъектам. Для нового файла, у которого нет посторонних явных ACE, получается требуемая DACL.

$akf = Join-Path $env:ProgramData 'ssh\administrators_authorized_keys'

icacls.exe $akf /inheritance:r `
    /grant '*S-1-5-32-544:F' `
    /grant '*S-1-5-18:F'

icacls.exe $akf

SID S-1-5-32-544 обозначает встроенную группу Administrators, а S-1-5-18 — LocalSystem. Звёздочка перед числовым SID нужна синтаксису icacls. Такой вариант не зависит от языка интерфейса Windows, тогда как Administrators:F на русской системе может не разрешиться из-за локализованного имени группы.

Есть тонкость: /inheritance:r удаляет унаследованные ACE, но не обязан удалять посторонние явные записи, созданные раньше. Поэтому вывод icacls.exe нужно читать, а не считать формальную успешность команды доказательством. Если остались явные разрешения другого пользователя или группы, их надо удалить либо сформировать DACL заново. Следующий PowerShell-код очищает правила доступа и создаёт разрешающие записи только для двух документированных SID.

$akf = Join-Path $env:ProgramData 'ssh\administrators_authorized_keys'
$acl = Get-Acl -LiteralPath $akf
$acl.SetAccessRuleProtection($true, $false)

foreach ($ace in @($acl.Access)) {
    [void]$acl.RemoveAccessRuleAll($ace)
}

foreach ($sidText in 'S-1-5-18', 'S-1-5-32-544') {
    $sid = New-Object System.Security.Principal.SecurityIdentifier($sidText)
    $rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
        $sid,
        'FullControl',
        'Allow'
    )
    [void]$acl.AddAccessRule($rule)
}

Set-Acl -LiteralPath $akf -AclObject $acl
icacls.exe $akf

Не пытайтесь обходить это требование директивой StrictModes no. Microsoft относит StrictModes к параметрам, недоступным во встроенной Windows-сборке. Нельзя рассчитывать, что неподдерживаемая директива отключит проверку; кроме того, неизвестный конкретной сборке параметр способен сорвать проверку конфигурации. Исправлять нужно DACL, а после любой правки конфига запускать sshd.exe -t.

Не выдавайте доступ конкретному сервисному пользователю, Users или Authenticated Users «на всякий случай». В DACL административного файла должны остаться только SYSTEM и BUILTIN\Administrators.
Памятка: ACL administrators_authorized_keys без опасных допусков — схема
Памятка: ACL administrators_authorized_keys без опасных допусков. Открыть схему в полном размере

Кодировка и структура строки публичного ключа

Правильный путь и DACL не помогут, если файл записан в неподходящей кодировке. Во встроенной Windows PowerShell 5.1 операторы перенаправления > и >>, как и Out-File без явного -Encoding, создают UTF-16LE. Поэтому привычная команда, которая в другой оболочке выглядит безобидно, может породить последовательность байтов, не соответствующую формату строк authorized_keys. В PowerShell версии 6 и новее правила кодировки изменились, но серверный скрипт лучше делать независимым от версии оболочки.

Для открытых ключей Ed25519, состоящих из ASCII-символов, надёжный вариант в Windows PowerShell 5.1 — Set-Content -Encoding ascii при первичном создании и Add-Content -Encoding ascii при осознанном добавлении к уже проверенному ASCII-файлу. Не добавляйте новую строку к файлу неизвестной кодировки: сначала сохраните резервную копию, прочитайте существующие ключи и перепишите итоговый набор единообразно.

$source = Join-Path $env:USERPROFILE '.ssh\id_ed25519.pub'
$target = Join-Path $env:ProgramData 'ssh\administrators_authorized_keys'

$publicKey = (Get-Content -LiteralPath $source -Raw).Trim()
if ($publicKey -notmatch '^ssh-ed25519\s+[A-Za-z0-9+/]+={0,3}(\s+.*)?$') {
    throw 'Файл не похож на однострочный открытый ключ Ed25519.'
}

Set-Content -LiteralPath $target -Value $publicKey -Encoding ascii

$bytes = [System.IO.File]::ReadAllBytes($target)
[System.BitConverter]::ToString($bytes[0..3])
ssh-keygen.exe -lf $target

Первые байты корректной строки Ed25519 соответствуют ASCII-тексту ssh- и выводятся как 73-73-68-2D. Последовательность FF-FE указывает на BOM UTF-16LE, а EF-BB-BF — на BOM UTF-8. Для совместимости я храню authorized_keys в ASCII, если комментарии тоже ASCII, либо в UTF-8 без BOM, когда нужны не-ASCII-комментарии. Сам ключ всё равно состоит из ASCII-символов.

Каждый открытый ключ занимает одну строку: тип, закодированные данные и необязательный комментарий. Перенос внутри base64-части делает запись недействительной. При копировании через RDP, мессенджер или почту я обязательно сравниваю отпечаток исходного ключа и серверного файла командой ssh-keygen.exe -lf. Если общий файл содержит несколько ключей, утилита выводит отпечаток каждой распознанной строки.

Не доверяйте внешнему виду файла в редакторе: BOM и нулевые байты там не видны. Проверяйте первые байты и отпечаток ключа.

Разбор практики: строительная компания «Монолит-Инвест», 70 РМ и три площадки

Условный клиент — строительная компания «Монолит-Инвест», 70 РМ и три площадки: центральный офис, строительная площадка и арендованный узел в ЦОД. В инфраструктуре работали четыре сервера. Летом 2026 года новый сервер приложений на Windows Server 2025 Standard заменил систему на Windows Server 2016. Через него шла файловая работа с проектной документацией, а ночная задача забирала выгрузку из 1С и передавала архив на внешний узел по SFTP. Скрипт PowerShell использовал отдельный ключ Ed25519 без интерактивного ввода.

Нас подключили после того, как архивы не поступали три недели. Задача планировщика запускалась, но SFTP-сессия не проходила аутентификацию. В ручном запуске клиента с подробным выводом было видно, что Ed25519-ключ предлагается, после чего сервер оставляет только парольный метод. Это сразу сузило область поиска: сеть, порт и обмен ключом узла работали, а отказ происходил на проверке пользовательского открытого ключа.

Публичный ключ находился в C:\Users\svc_backup\.ssh\authorized_keys. Сама запись была корректной, но svc_backup входила в локальную группу администраторов. Права добавили полтора года назад ради доступа прежней версии задачи к теневым копиям и после изменения процесса не отозвали. На старом сервере проблема не проявлялась: WinSCP использовал сохранённый пароль. При переходе на ключевую аутентификацию сервер начал применять административный Match-блок и искать другой файл.

Исправление заняло двадцать минут. Ключ перенесли в C:\ProgramData\ssh\administrators_authorized_keys, ACL назначили через SID, поскольку сервер был русскоязычным. Первую попытку файла пришлось заменить: его создали оператором перенаправления Windows PowerShell 5.1, поэтому он оказался в UTF-16LE. Корректная ASCII-запись занимала 92 байта, содержала один ключ, а её SHA256-отпечаток совпал с исходным.

Для контрольного подключения временно включили SyslogFacility LOCAL0 и LogLevel DEBUG3, проверили конфигурацию, перезапустили службу и увидели принятие ключа в журнале. После восстановления выгрузки мы вернулись к причине, а не только к симптому: сервисной учётной записи больше не требовались административные права. Её удалили из Administrators, ключ вернули в профильный authorized_keys, а общий файл оставили для реального административного доступа.

Выгрузка прошла той же ночью, а требуемая глубина резервных копий восстановилась за четверо суток. Число административных учётных записей на сервере сократилось с шести до двух. Для меня это главный итог разбора: перенос ключа восстановил задачу, а пересмотр полномочий устранил лишний путь привилегированного входа сразу на трёх площадках.

До настройки общего административного файла проверьте, нужны ли этой учётной записи права администратора. Для фоновой задачи почти всегда безопаснее выдать точечные разрешения и использовать профильный authorized_keys.
Памятка: Разбор практики: строительная компания «Монолит-Инвест», 70 РМ и три площадки — схема
Памятка: Разбор практики: строительная компания «Монолит-Инвест», 70 РМ и три площадки. Открыть схему в полном размере

Проверенный рецепт для Windows Server 2025

В Windows Server 2025 OpenSSH установлен по умолчанию, но серверная служба не обязана быть включена. В Server Manager на странице Local Server предусмотрен переключатель Remote SSH Access. То же действие выполняют Start-Service и Set-Service. Для Windows Server 2019 и Windows Server 2022 серверный компонент обычно добавляют как возможность с идентификатором OpenSSH.Server~~~~0.0.1.0; хвост 0.0.1.0 является именем Windows Capability, а не заявлением о версии бинарника OpenSSH.

Установка OpenSSH Server создаёт правило брандмауэра OpenSSH-Server-In-TCP, разрешающее входящий TCP-порт 22. Политика безопасности могла удалить или отключить его, поэтому проверка нужна. Если правило отсутствует или порт закрыт на периметре, соединение будет отклонено или сброшено; повторный запрос пароля означает, что транспортное соединение уже установилось и искать причину нужно на уровне аутентификации.

Get-Service -Name sshd
Set-Service -Name sshd -StartupType Automatic
Start-Service -Name sshd

$firewallRule = Get-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -ErrorAction SilentlyContinue
if (-not $firewallRule) {
    New-NetFirewallRule `
        -Name 'OpenSSH-Server-In-TCP' `
        -DisplayName 'OpenSSH Server (sshd)' `
        -Enabled True `
        -Direction Inbound `
        -Protocol TCP `
        -Action Allow `
        -LocalPort 22
} elseif ($firewallRule.Enabled -ne 'True') {
    Enable-NetFirewallRule -Name 'OpenSSH-Server-In-TCP'
}

Ключ генерируется на клиенте. Приватная часть остаётся там же; на сервер передаётся только файл с расширением .pub. Microsoft Learn указывает, что при запуске ssh-keygen без выбора алгоритма используется Ed25519, но я всё равно задаю -t ed25519 явно: так назначение команды видно в скрипте и журнале изменений.

ssh-keygen.exe -t ed25519 -f "$env:USERPROFILE\.ssh\id_ed25519"
ssh-keygen.exe -lf "$env:USERPROFILE\.ssh\id_ed25519.pub"

На сервере я сначала принимаю путь к реальному публичному ключу, проверяю формат, записываю файл в ASCII и назначаю документированный ACL. В примере файл создаётся заново для первичной настройки. Если он уже содержит действующие ключи других администраторов, их необходимо предварительно сохранить, проверить и объединить: бездумный Set-Content перезапишет существующее содержимое.

param(
    [Parameter(Mandatory = $true)]
    [string]$PublicKeyPath
)

$akf = Join-Path $env:ProgramData 'ssh\administrators_authorized_keys'
$pub = (Get-Content -LiteralPath $PublicKeyPath -Raw).Trim()

if ($pub -notmatch '^(ssh-ed25519|ecdsa-sha2-nistp256|ssh-rsa)\s+\S+(\s+.*)?$') {
    throw 'Не удалось распознать строку открытого ключа.'
}

Set-Content -LiteralPath $akf -Value $pub -Encoding ascii
icacls.exe $akf /inheritance:r `
    /grant '*S-1-5-32-544:F' `
    /grant '*S-1-5-18:F'

icacls.exe $akf
ssh-keygen.exe -lf $akf

Контрольный вход выполняю в новой параллельной сессии, явно указывая приватный ключ и запрещая клиенту переходить к паролю. Флаг -i выбирает файл удостоверения, -o PasswordAuthentication=no исключает ложное впечатление успеха за счёт пароля, а -vvv включает максимальную клиентскую детализацию. Только после успешного теста можно обсуждать отключение паролей на сервере.

ssh.exe -vvv `
    -i "$env:USERPROFILE\.ssh\id_ed25519" `
    -o PasswordAuthentication=no `
    adminuser@server01
Не отключайте парольную аутентификацию, пока вход по ключу не подтверждён в отдельной сессии. Сохраните доступ к консоли или действующую административную сессию на время изменения.

Диагностика по журналу и безопасное завершение настройки

Если путь, строка ключа и DACL выглядят правильно, я включаю серверный журнал. При стандартном значении SyslogFacility AUTH Windows OpenSSH направляет события в ETW. Для временного файлового журнала Microsoft предписывает SyslogFacility LOCAL0; файлы появляются в %ProgramData%\ssh\logs. Уровень DEBUG3 даёт подробный трассировочный вывод, но использовать его постоянно не следует.

# C:\ProgramData\ssh\sshd_config
SyslogFacility LOCAL0
LogLevel DEBUG3

После изменения сначала проверяю конфиг, затем перезапускаю службу и наблюдаю журнал. Имя серверного файла — sshd.log. Если он ещё не создан, нужно выполнить хотя бы одну попытку подключения после перезапуска.

$config = Join-Path $env:ProgramData 'ssh\sshd_config'
$sshd = Join-Path $env:WINDIR 'System32\OpenSSH\sshd.exe'

& $sshd -t -f $config
if ($LASTEXITCODE -ne 0) {
    throw 'Ошибка в sshd_config.'
}

Restart-Service -Name sshd
Get-Content -LiteralPath "$env:ProgramData\ssh\logs\sshd.log" -Tail 100 -Wait

Параллельно запускаю клиент с -vvv. Сообщение о том, что клиент предлагает ключ, доказывает лишь наличие и выбор приватного ключа на клиенте. Оно не доказывает, что сервер прочитал нужный файл или принял открытую часть. Серверный журнал позволяет увидеть вычисленный путь, проблемы разбора и отказ из-за разрешений; именно его я считаю главным источником при спорной диагностике.

После проверки возвращаю LogLevel INFO. Я не обещаю универсальный срок заполнения диска: объём зависит от числа подключений, сканирования порта и ротации журналов. Но DEBUG3 заметно увеличивает поток записей и может занять системный том, если за каталогом никто не следит. Это достаточная причина не оставлять отладку включённой.

Есть ещё несколько ограничений Windows OpenSSH. Ключевая аутентификация поддерживается для локальных учётных записей и доменных учётных записей Active Directory, но не для Microsoft Entra ID. Директивы AuthorizedKeysCommand и AuthorizedKeysCommandUser недоступны, поэтому привычную Linux-схему динамической загрузки ключей из каталога нельзя переносить буквально. GSSAPIAuthentication доступна начиная с Windows Server 2022, однако это отдельный способ аутентификации и не исправляет неверный AuthorizedKeysFile.

Правила допуска обрабатываются в порядке DenyUsers, AllowUsers, DenyGroups, AllowGroups. Microsoft требует записывать имена учётных записей в таких правилах строчными буквами. Доменные субъекты разрешаются в форме короткое_имя_домена\имя, а для шаблонов с полным доменным именем документация приводит форму user?domain*: вопросительный знак заменяет символ, разделяющий имя и домен, поскольку @ уже используется в шаблоне user@host. Ошибка в Allow- или Deny-правиле может заблокировать вход независимо от исправности ключа.

После успешного теста я ограничиваю доступ подходящей группой, отключаю парольный метод только при наличии проверенного аварийного доступа, закрываю TCP-порт 22 от всего интернета на периметре и выдаю отдельный ключ каждой автоматизированной задаче. Общий administrators_authorized_keys включаю в регулярную ревизию: из него надо удалять ключи завершённых задач и сотрудников, потерявших административный доступ.

После диагностики верните `LogLevel INFO`, перезапустите sshd и проверьте новый вход. Отладочный журнал не должен оставаться постоянной настройкой.
Памятка: Диагностика по журналу и безопасное завершение настройки — схема
Памятка: Диагностика по журналу и безопасное завершение настройки. Открыть схему в полном размере

Частые вопросы

Куда класть ключ доменной учётной записи, входящей в администраторы сервера?

При стандартном `Match Group administrators` — в `C:\ProgramData\ssh\administrators_authorized_keys`. Проверьте локальный `%ProgramData%\ssh\sshd_config` и журнал, поскольку Match-блок могли изменить. Ключевая аутентификация поддерживается для доменных учётных записей Active Directory, но не для Microsoft Entra ID.

Можно ли отключить проверку ACL через StrictModes no?

Нет, полагаться на это нельзя. Microsoft относит `StrictModes` к недоступным параметрам встроенной Windows-сборки OpenSSH. Исправьте DACL так, чтобы в ней остались только SYSTEM и BUILTIN\Administrators, а после изменения конфига обязательно запустите `sshd.exe -t`.

Почему icacls не распознаёт Administrators:F на русской Windows?

Имя встроенной группы локализовано. Используйте числовые SID со звёздочкой: `*S-1-5-32-544:F` для Administrators и `*S-1-5-18:F` для SYSTEM. Синтаксис не зависит от языка ОС.

Достаточно ли выполнить icacls с /inheritance:r и двумя /grant?

Для нового файла с унаследованными разрешениями обычно да, но `/inheritance:r` не гарантирует удаление ранее созданных явных ACE. После команды изучите вывод `icacls.exe`. Если в DACL остаются другие субъекты, удалите их или сформируйте правила заново.

Почему нельзя создавать administrators_authorized_keys через оператор >?

Во встроенной Windows PowerShell 5.1 операторы `>` и `>>` используют `Out-File` и записывают UTF-16LE. Для открытого ключа задайте `Set-Content -Encoding ascii` либо создайте UTF-8 без BOM другим проверенным способом. Затем сравните отпечаток через `ssh-keygen.exe -lf`.

Нужно ли перезапускать sshd после изменения ключей или ACL?

После изменения `authorized_keys` и ACL перезапуск обычно не нужен: данные проверяются при новой попытке входа. Изменения `%ProgramData%\ssh\sshd_config` требуют проверки синтаксиса и `Restart-Service -Name sshd`.

Нужно ли устанавливать OpenSSH Server в Windows Server 2025?

Он установлен по умолчанию, но не обязательно включён. Запустите `sshd` через Remote SSH Access в Server Manager или командами `Start-Service` и `Set-Service`. На Windows Server 2019 и 2022 компонент обычно устанавливают как возможность `OpenSSH.Server~~~~0.0.1.0`.

Как проверить ключ, не войдя случайно по паролю?

На клиенте укажите приватный ключ через `-i` и добавьте `-o PasswordAuthentication=no`. Для подробного вывода используйте `-vvv`. Если ключ не принят, клиент завершит попытку вместо перехода к парольной аутентификации.

Столкнулись с похожей задачей? Обращайтесь — решим

Если у вас происходит что-то из описанного в этой статье — или любая другая проблема с ИТ-инфраструктурой, — обращайтесь в любое время. Мои специалисты и я лично разберём ситуацию, найдём настоящую причину и доведём до решения.

Возьмёмся и за разовую задачу, и за постоянное обслуживание. Первичная консультация — бесплатно и без обязательств.

📞 +7 903 729-62-41 💬 MAX: +7 903 729-62-41 ✈ Telegram @ITfresh_Boss

С уважением, Семёнов Евгений Сергеевич, директор «АйТи Фреш» — IT-аутсорсинг для компаний до 50 рабочих мест, 15+ лет практики

Источники

© ООО «АйТи-Фреш» · Москва · Все статьи