Настройка Kerberos аутентификации в ЕПП, ASP.NET#

Исходные данные#

Имеется сервер контроллера домена FreeIPA:

  • имя домена astra.aaa

  • администратор домена admin@astra.aaa

  • пользователь домена user-01@astra.aaa

  • имя сервера dc-01.astra.aaa

  • сервер имеет постоянный IP-адрес, например, 192.168.1.20

Веб-сервер располагается отдельно:

  • имя сервера websrv-01.astra.aaa

  • сервер должен быть введен в домен

  • на сервере установлен и настроен web-сервер Apache2

  • сервер имеет постоянный IP-адрес, например, 192.168.1.21

Сервер базы данных располагается отдельно:

  • имя сервера dbsrv-01.astra.aaa

  • сервер должен быть введен в домен

  • на сервере должна быть установлена и настроенная СУБД Postgresql

  • сервер имеет постоянный IP-адрес, например, 192.168.1.23

Пользовательский компьютер располагается на отдельном компьютере:

  • имя компьютера pc-01.astra.aaa

  • компьютер должен быть введен в домен

  • компьютер имеет постоянный IP-адрес, например, 192.168.1.22

Основная концепция, реализация веб-приложения на ASP.NET#

Пользователь домена со своего компьютера отправляет запрос c аутентификацией Kerberos, на сервер приложения Apache2.

Веб сервер Apache2 производит аутентификацию пользователя, если аутентификация прошла успешно, то проксирует запрос с добавленными заголовками на веб сервер Kestrel и создает делегируемый Kerberos кэш, если аутентификации прошла неуспешно выдает ошибку.

Веб сервер Kestrel передаёт запрос веб приложению ASP.NET.

Веб приложение ASP.NET добавляет в окружение переменную KRB5CCNAME и в строку подключения к базе данных имя пользователя, взятые из заголовка запроса.

Далее драйвер Npgsql, в режиме GSS, производит запрос к БД Postgresql.

БД производит аутентификацию, если требуется авторизацию и отдаёт данные пользователю.

Предупреждение

Для изменения системной переменной KRB5CCNAME из приложения ASP.NET необходимо установить NuGet пакет KerberosCCache, реализованный с использованием P/Invoke (подробнее в инстркукции по созданию пакета).

P/Invoke (Platform Invoke) — это механизм в .NET, позволяющий вызывать функции из нативных библиотек, написанных на C/C++ или других языках, компилируемых в машинный код.

Настройка компьютера пользователя#

Для настройки компьютера пользователя необходимо перейти по ссылке и выполнить действия по инструкции:

Настройка сервера базы данных#

Для настройки сервера базы данных необходимо перейти по ссылке и выполнить действия по инструкции:

Установка и настройка веб-сервера Apache2#

Для установки и настройки веб-сервера Apache2 необходимо перейти по ссылке и выполнить действия по инструкции:

Установка веб-приложения#

Для установки и настройки веб-приложения необходимо:

Пункт 1#

  • установить следующие пакеты пакет:

sudo apt install dotnet8

Пункт 2#

  • создание приложения из шаблона и перейти в созданный каталог:

dotnet new web -n WebApp

Пункт 3#

  • загрузить драйвер для работы с СУБД PostgreSQL:

dotnet add package Npgsql

Пункт 4#

  • добавить в проект nuget-пакет KerberosCCache:

dotnet add package KerberosCCache

Примечание

Добавление пакета может завершиться ошибкой. Рекомендуется пересобрать проект и проверить список добавленных пакетов:

dotnet restore
dotnet list package

Если пакета нет в списке, повторить сборку командой из пункта 4.

Пункт 5#

  • файл Program.cs должен иметь следующее содержимое:

using System;
using Npgsql;
using KerberosCCache;

namespace WebApp;

public class Program
{
   public static void Main(string[] args)
   {
      var builder = WebApplication.CreateBuilder(args);
      var app = builder.Build();

     // URL-адрес обработки запроса с использованием Kerberos кеша
      app.MapGet("/", async(HttpContext context) => await Get(context));
      app.Run();
   }

   /// <summary>
   /// Обрабатывает аутентифицированный запрос, переключаясь на кэш запросов пользователя Kerberos
   /// и подключаясь к базе данных PostgreSQL с использованием аутентификации GSSAPI (Kerberos).
   /// </summary>
   /// <param name="context">Текущий HTTP контекст.</param>


   static async Task Get(HttpContext context)
   {
       // ⚠️ SECURITY
       // Заголовки с X_KRB5CCNAME и X_GSS_NAME должны приниматься ТОЛЬКО доверенным прокси-сервером
       // (например, Apache с mod_auth_kerb или nginx с модулем SPNEGO).
       // Никогда не принимайте запросы от недоверенных клиентов!

       if (!context.Request.Headers.TryGetValue("X_KRB5CCNAME", out var krb5_path) ||
            !context.Request.Headers.TryGetValue("X_GSS_NAME", out var user))
       {
            context.Response.StatusCode = 400;
            await context.Response.WriteAsync("Пропущены параметры аутентификации X_KRB5CCNAME и X_GSS_NAME в заголовке.");
            return;
       }

       string html = @"
         <!DOCTYPE html>
         <html lang=""ru"">
         <head>
             <meta charset=""UTF-8"">
             <title>Title</title>
         </head>
         <body>";

       NpgsqlConnection? con = null;

       try
       {
            // Установка клиентского кеша в KRB5CCNAME
            KerberosCache.SetKrb5Cache(krb5_path);

            // Строка подключения к PostgreSQL с использованием аутентификации GSSAPI
            string cs = $"Host=dbsrv-01.astra.aaa;Port=5432;Database=postgres;Include Realm=true;Username={user};";

            con = new NpgsqlConnection(cs);
            await con.OpenAsync();

            html += "<div>Подключение успешно установлено</div>";
            using (var cmd = new NpgsqlCommand("SELECT VERSION();", con))
            {
               var version = await cmd.ExecuteScalarAsync();
               html += $"<div>Версия PostgreSQL: {version}</div>";
               html += $"<div>User: {user}</div>";
            }
       }
       catch (PlatformNotSupportedException ex)
       {
           html = $"<div>Платформа не поддерживается: {ex.Message}</div>";
       }
       catch (InvalidOperationException ex)
       {
           html = $"<div>Ошибка задания KRB5CCNAME: {ex.Message}</div>";
       }
       catch (Exception ex)
       {
           html = $"<div>Ошибка при подключении к PostgreSQL: {ex.Message}</div>";
       }
       finally
       {
           if (con is not null)
           {
               await con.DisposeAsync();
           }
           html += "<div>Подключение закрыто</div>";
           html += "</body></html>";
       }
       context.Response.ContentType = "text/html; charset=utf-8";
       await context.Response.WriteAsync(html);
   }
}

Пункт 6#

  • публикация приложения:

dotnet publish -c Release -o out -r linux-x64 --self-contained

Пункт 7#

  • создать файл /etc/systemd/system/webapp.service, для systemd-сервиса, чтобы приложение автоматически запускался при старте сервера:

[Unit]
Description=WebApp
After=network.target

[Service]
WorkingDirectory=/var/www/html/WebApp
ExecStart=/usr/bin/dotnet /var/www/html/WebApp/out/WebApp.dll
Restart=always
RestartSec=10
KillSignal=SIGINT
SystemlogIdentifier=WebApp
User=www-data
Group=www-data
Environment=ASPNETCORE_ENVIRONMENT=Production

[Install]
WantedBy=multi-user.target

Пункт 8#

  • запуск сервиса и его добавление в автозапуск:

sudo systemctl daemon-reload
sudo start webapp
sudo systemctl enable webapp

Тестирование веб-приложения#

Пункт 1#

  • c компьютера пользователя запустите предварительно настроенный браузер и вставьте следующую ссылку:

http:\\websrv-01.astra.aaaa

Пункт 2#

  • возвращённая страница должна содержать ответ:

PostgreSQL 15.6 (Debian 15.6-astra.se2+b1) on x86_64-pc-linux-gnu, compiled by gcc (Astra 12.2.0-14.astra3+b1) 12.2.0, 64-bit

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