Разработка собственного NuGet пакета#
Особенности разработки в Astra Linux Special Edition#
В Linux каждый процесс имеет:
UID/GID (пользователь/группа)
Environment variables (включая HOME, PATH, KRB5CCNAME)
Working directory
Capabilities (ограниченные привилегии, даже у root)
Namespaces (в контейнерах или через unshare)
cgroups (ограничение ресурсов)
Основные аспекты безопасности и прав:
изменение критического контекста (пользователь, изоляция) должно происходить до запуска основного приложения (можно вносить некоторые изменения в runtime, например, установка переменных окружения или изменение рабочей директории, если процесс имеет соответствующие привилегии);
переменные окружения в Linux — глобальное состояние процесса, а не потока (даже если изменять переменные с помощью setenv() в разных Task или Thread — они разделяют одну и ту же память окружения);
рекомендуется использовать systemd (или эквивалентный менеджер процессов) для запуска служб .NET в production;
в Linux используется чувствительная к регистру файловая система.
Примечание
В Windows SetEnvironmentVariable тоже глобален для процесса, но .NET эмулирует «локальные» переменные через внутренний словарь — но только в рамках управляемого кода. На Linux такой эмуляции нет по умолчанию.
Создание NuGet пакета для Kerberos#
Решаемая задача#
Для настройки Kerberos аутентификации в ЕПП необходимо обновить переменную окружения KRB5CCNAME
без перезапуска .NET-сервиса. Однако стандартный метод Environment.SetEnvironmentVariable(...) изменяет переменную
окружения только внутри текущего процесса, и native-библиотека GSSAPI/Kerberos не перечитывает значение.
Для решения этой проблемы был создан NuGet пакет KerberosCCache.
Описание пакета#
Пакет KerberosCCache был написан для управления переменной окружения KRB5CCNAME в .NET-приложениях на Linux, чтобы
передавать в заголовке Kerberos-билет, полученный от пользователя, при подключении к сервисам, поддерживающим аутентификацию
с GSSAPI/SSPI (например, PostgreSQL).
Подробнее о пакете Krb5CCache (доступен для net6.0, net7.0, net8.0, net9.0, net10.0).
Сборка пакета#
Пункт 1#
создать проект библиотеки:
dotnet new classlib -n KerberosEnvironmentLib
cd KerberosEnvironmentLib
Пункт 2#
удалить существующий в каталоге файл
Class1.csи создать файлNativeEnvironment.csсо следующим содержимым:
using System;
using System.Runtime.InteropServices;
namespace KerberosCCache;
/// <summary>
/// Утилита для управления переменной среды KRB5CCNAME, которая указывает на файл кэша учетных данных Kerberos.
/// </summary>
public static class KerberosCache
{
// Импорт нативной функции установки переменных
[DllImport("libc", SetLastError = true)]
private static extern int setenv(string name, string value, int overwrite);
/// <summary>
/// Устанавливает переменную среды KRB5CCNAME, которая указывает на файл кэша учетных данных Kerberos.
/// </summary>
/// <param name="cachePath"> Путь к Kerberos credentials cache (e.g., /tmp/krb5cc_1000).</param>
/// <param name="overwrite"> Если значение true, то существующее значение перезаписывается.</param>
/// <exception cref="PlatformNotSupportedException">
/// Возникает, когда операционная система не является Linux.
/// </exception>
/// <exception cref="InvalidOperationException">
/// Возникает при сбое вызова setenv.
/// </exception>
/// <remarks>
/// Переменная окружения <c>KRB5CCNAME</c> используется библиотеками Kerberos (например, MIT Kerberos, GSSAPI),
/// чтобы указать путь к кэшу тикетов. Изменение этой переменной влияет на все последующие вызовы,
/// использующие Kerberos-аутентификацию в текущем процессе и его дочерних процессах.
/// </remarks>
/// <example>
/// <code>
/// KerberosCache.SetKrb5Cache("/tmp/krb5cc_1000");
/// </code>
/// </example>
public static void SetKrb5Cache(string cachePath, bool overwrite = true)
{
if (OperatingSystem.IsLinux())
{
int result = setenv("KRB5CCNAME", cachePath, overwrite ? 1 : 0);
if (result != 0)
{
int errno = Marshal.GetLastWin32Error();
throw new InvalidOperationException($"Failed to set KRB5CCNAME. Error: {errno}");
}
}
else
{
throw new PlatformNotSupportedException(
"Setting KRB5CCNAME is supported only on Linux. " +
"Other platform use their own security mechanisms.");
}
}
}
Примечание
В библиотеке используется технология P/Invoke (Platform Invocation Services)
для вызова нативных функций операционной системы из управляемого кода .NET.
В данном случае — установка переменной окружения через системный вызов setenv().
P/Invoke — это механизм в .NET, позволяющий вызывать функции из нативных библиотек,
написанных на C/C++ или других языках, компилируемых в машинный код.
Объяснение кода#
Хотя .NET предоставляет функционал изменения переменных окружения Environment.SetEnvironmentVariable(string, string),
эта функция НЕ влияет на поведение некоторых нативных библиотек, таких как MIT Kerberos, GSSAPI, OpenSSH, libcurl с поддержкой GSS.
Эти библиотеки читают переменную KRB5CCNAME напрямую из окружения процесса при старте,
а не из внутреннего кэша .NET. В связи с этим необходимо использовать нативный вызов setenv(),
чтобы гарантировать, что все компоненты видят обновлённое значение (поскольку setenv() меняет переменную окружения
для всего процесса, а не для потока).
Ограничения и особенности использования P/Invoke#
Код работает только на Unix-подобных системах (Linux, macOS), где доступна библиотека
libc. На Windows используется другая модель безопасности (LSA), и переменнаяKRB5CCNAMEне применяется.Изменение переменных окружения может повлиять на дочерние процессы. Убедитесь, что путь к кэшу существует и доступен для чтения текущим пользователем.
Атрибут
[DllImport]должен содержатьSetLastError = true, чтобы корректно получить код ошибки (черезMarshal.GetLastWin32Error). Без этого ошибка будет потеряна.Значение ошибки
errnoдолжно быть прочитано сразу после P/Invoke-вызова, так как любые последующие вызовы (даже управляемые) могут его перезаписать.Нет поддержки async: P/Invoke вызовы блокирующие. Не следует использовать в высоконагруженных асинхронных сценариях без обёртки в Task.Run.
Пункт 3#
сборка библиотеки (после сборки библиотека будет находиться в
bin/Release/net8.0/KerberosCCache.dll):
dotnet build --configuration Release
Пункт 4#
упаковка в NuGet (
*.nupkgбудет располагаться вbin/Release) и публикация собранного*.nugetпакета в NuGet.org:
dotnet pack --configuration Release
Пункт 5#
после получения nuget пакета его можно подключить в другой .Net проект (если пакет расположен локально, указать дополнительно параметр
--source/путь/к/nupkgs):
dotnet add package KerberosCCache