Публікація

Налаштування заголовків перенаправлення в .NET при роботі з Azure Application Gateway

Налаштування заголовків перенаправлення в .NET при роботі з Azure Application Gateway

Коли ми розгортаємо .NET додаток на Azure App Service та налаштовуємо доступ до нього через Azure Application Gateway, виникає типова проблема з визначенням правильного хосту та схеми запиту. Суть проблеми в тому, що запит проходить кілька рівнів до вашого додатка:

  • Користувач відправляє запит на https://myapp.example.com
  • Запит потрапляє до Application Gateway
  • Application Gateway перенаправляє запит до App Service
  • App Service передає запит вашому додатку

На кожному етапі інформація про оригінальний запит (IP-адреса користувача, схема, хост) може бути втрачена або замінена на внутрішню. В результаті, коли запит досягає вашого додатка, HttpContext містить не ті дані, які були в оригінальному запиті. Наприклад, якщо ваш користувач заходить через https://myapp.example.com, то до вашого додатка запит може дійти як http://internal-appservice-ip:port. Це призводить до таких проблем:

  • Неправильна генерація URL - всі посилання, які генерує ваш додаток, будуть містити неправильний хост та схему
  • Помилки автентифікації - особливо з OpenIddict або іншими реалізаціями OpenID Connect, які суворо перевіряють URL перенаправлення
  • Проблеми з CORS - неправильне визначення Origin-заголовка
  • Помилки в роботі middleware - багато компонентів .NET залежать від правильного визначення схеми та хоста

Рішення

.NET має вбудований механізм для обробки перенаправлених заголовків, який можна налаштувати для коректної роботи з проксі-серверами.

Нижче наведено код, який вирішує проблему:

1
2
3
4
5
6
7
8
9
10
11
12
services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedHeaders = 
       ForwardedHeaders.XForwardedFor | 
       ForwardedHeaders.XForwardedProto |
       ForwardedHeaders.XForwardedHost;
    
    options.ForwardedHostHeaderName = "X-Original-Host";
    
    options.KnownNetworks.Clear();
    options.KnownProxies.Clear();
});

Розберемо, що відбувається в цьому коді:

Налаштування ForwardedHeaders

1
2
3
4
options.ForwardedHeaders = 
   ForwardedHeaders.XForwardedFor | 
   ForwardedHeaders.XForwardedProto |
   ForwardedHeaders.XForwardedHost;

Тут ми вказуємо, які саме заголовки перенаправлення повинні оброблятися:

  • XForwardedFor - визначає оригінальну IP-адресу клієнта
  • XForwardedProto - визначає оригінальний протокол (http або https)
  • XForwardedHost - визначає оригінальний хост, вказаний у запиті

Налаштування додаткового заголовка хосту

1
options.ForwardedHostHeaderName = "X-Original-Host";

Цей рядок є критично важливим для нашого сценарію з Azure Application Gateway. Він вказує .NET дивитися на нестандартний заголовок X-Original-Host для визначення оригінального хосту. Часто Azure Application Gateway або інші проксі налаштовані на передачу оригінального хосту саме в цьому заголовку замість стандартного X-Forwarded-Host.

Очищення списків довірених мереж і проксі

1
2
options.KnownNetworks.Clear();
options.KnownProxies.Clear();

За замовчуванням, .NET приймає перенаправлені заголовки тільки від довірених проксі для безпеки. Очищаючи ці списки, ми дозволяємо приймати заголовки від будь-яких джерел.

Приклад проблеми з OpenIddict

Розглянемо конкретний приклад проблеми з OpenIddict:

Припустимо, ваш додаток розгорнуто за Application Gateway, і користувачі отримують доступ до нього за адресою https://myapp.example.com. Однак, сам додаток бачить запити, що надходять від Application Gateway з локальної мережі, наприклад, як http://internal-ip:port. При налаштуванні OpenIddict, ви вказуєте URL перенаправлення як https://myapp.example.com/signin-callback. Коли користувач проходить автентифікацію, OpenIddict намагається перевірити, чи збігається URL перенаправлення з налаштованим. Але оскільки додаток “думає”, що працює за адресою http://internal-ip:port, він генерує URL перенаправлення як http://internal-ip:port/signin-callback.

Результат: несумісність URL призводить до помилки автентифікації з повідомленням на кшталт "Invalid redirect_uri".

Застосування рішення в Startup.cs

Для повного вирішення проблеми, крім налаштування опцій, необхідно також додати middleware для обробки перенаправлених заголовків у метод Configure:

1
2
 // Важливо додати це на самому початку пайпайна обробки запитів
app.UseForwardedHeaders();

Особливості налаштування для Azure App Service

Додатково варто зазначити, що коли ваш додаток розгорнуто на Azure App Service за Application Gateway, проблема з перенаправленням заголовків стає особливо актуальною. App Service має власну інфраструктуру балансування навантаження, яка додає ще один рівень проксіювання:

Користувач → Application Gateway → App Service infrastructure → Ваш додаток

Кожен рівень може модифікувати заголовки, тому правильне налаштування ForwardedHeadersOptions є критичним для забезпечення коректної роботи додатка в такій архітектурі. Також при розгортанні на App Service рекомендується перевірити налаштування ssl termination. Якщо SSL-термінація відбувається на рівні Application Gateway, то до вашого додатка запити можуть надходити по HTTP, навіть якщо клієнт використовує HTTPS. У такому випадку правильна обробка XForwardedProto забезпечить коректне визначення схеми.

Висновок

Правильне налаштування перенаправлених заголовків є критично важливим при розгортанні .NET додатків на Azure App Service за проксі-серверами, особливо якщо ви використовуєте протоколи автентифікації, такі як OpenID Connect. Наведене рішення дозволяє додатку коректно визначати оригінальну адресу запиту, що забезпечує правильну роботу URL-генерації, автентифікації та інших компонентів, які залежать від точного визначення хоста та схеми запиту.

Не забудьте в реальному сценарії налаштувати список довірених проксі для підвищення безпеки вашого додатку.

Публікація захищена ліцензією CC BY 4.0 .