Разработка собственного 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

Разработка на C##