Asp.Net Core Google аутентификация
Мое приложение работает на Google Compute Engine. Nginx используется в качестве прокси-сервера. Nginx был настроен на использование SSL. Ниже приведено содержимое /etc/nginx/sites-available/default:
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name mywebapp.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2 default_server;
listen [::]:443 ssl http2 default_server;
include snippets/ssl-mywebapp.com.conf;
include snippets/ssl-params.conf;
root /home/me/MyWebApp/wwwroot;
location /.well-known/ {
}
location / {
proxy_pass http://localhost:5000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
В Startup.cs у меня есть:
app.UseGoogleAuthentication(new GoogleOptions()
{
ClientId = Configuration["Authentication:Google:ClientId"],
ClientSecret = Configuration["Authentication:Google:ClientSecret"],
});
Теперь в Google Cloud Platform мне нужно указать URI авторизованного перенаправления. Если я введу следующее, мое веб-приложение будет работать как положено:
http://mywebapp.com/signin-google
Но это не сработает, если https
используется; браузер отображает следующую ошибку:
The redirect URI in the request, http://mywebapp.com/signin-google, does
not match the ones authorized for the OAuth client.
В этом случае безопасно ли использовать http в качестве авторизованного URI перенаправления? Какая конфигурация мне нужна, если я хочу, чтобы это был https?
2 ответа
Это происходит потому, что ваше приложение, которое работает за обратным прокси-сервером, не имеет ни малейшего представления, что первоначально запрос пришел по HTTPS.
Терминальный прокси SSL/TLS
Описанная в вопросе конфигурация обратного прокси-сервера называется обратным прокси-сервером завершения SSL/TLS. Это означает, что между клиентом и прокси-сервером устанавливается безопасный трафик. Прокси-сервер расшифровывает запрос и затем перенаправляет его в приложение по протоколу HTTP.
Проблема с этой конфигурацией состоит в том, что приложение позади нее не знает, что клиент отправил запрос по HTTPS. Поэтому, когда дело доходит до перенаправления к себе, он использует HttpContext.Request.Scheme
, HttpContext.Request.Host
а также HttpContext.Request.Port
создать действительный URL для перенаправления.
Заголовки X-Forwarded-* HTTP
Это где X-Forwarded-*
Заголовки вступают в игру. Чтобы приложение знало, что запрос изначально идет через прокси-сервер через HTTPS, нам нужно настроить прокси-сервер на X-Forwarded-For
а также X-Forwarded-Proto
Заголовки HTTP.
location / {
proxy_pass http://localhost:5000;
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
Хорошо, теперь, если мы вернемся к приложению ASP.NET Core и посмотрим на входящий HTTP-запрос, мы увидим оба X-Forwarded-*
заголовки установлены, однако URL перенаправления все еще использует схему HTTP.
Промежуточное ПО перенаправленных заголовков
В основном это промежуточное программное обеспечение переопределяет HttpContext.Request.Scheme
а также HttpContext.Connection.RemoteIpAddress
к значениям, которые были предоставлены X-Forwarded-Proto
а также X-Forwarded-For
заголовки соответственно. Чтобы это произошло, давайте добавим его в конвейер, добавив следующую строку где-то в начале Startup.Configure()
метод.
var forwardedHeadersOptions = new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto,
RequireHeaderSymmetry = false
};
forwardedHeadersOptions.KnownNetworks.Clear();
forwardedHeadersOptions.KnownProxies.Clear();
app.UseForwardedHeaders(forwardedHeadersOptions);
Это должно в конечном итоге заставить ваше приложение создавать допустимые URL-адреса по схеме HTTPS.
Моя история
Код выше выглядит иначе, чем предлагает Microsoft. Если мы посмотрим в документации, их код будет выглядеть немного короче:
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});
Однако это не сработало для меня. Также по комментариям под этим вопросом я не одинок.
У меня nginx, настроенный в качестве обратного прокси-сервера для приложения ASP.NET Core, работающего в Docker-контейнере. Все стало сложнее после того, как я поставил все за Amazon Load Balancer (ELB).
Сначала я последовал совету из документации, но у меня это не сработало. В моем приложении появилось следующее предупреждение:
Несоответствие количества параметров между X-Forwarded-For и X-Forwarded-Proto
Затем я посмотрел на X-Forwarded-*
Заголовки и понял, что они были разной длины. X-Forwarded-For
заголовок содержал 2 записи (IP-адреса, разделенные запятыми), а X-Forwarded-Proto
только одна запись https
, Вот как я подошел к настройке собственности RequireHeaderSymmetry
в false
,
Ну, я избавился от предупреждающего сообщения "Счетчик параметров...", но сразу после этого я столкнулся с другим странным отладочным сообщением:
Неизвестный прокси: 172.17.0.6:44624
Изучив исходный код Forwarded Headers Middleware, я наконец понял, что мне нужно либо очистить оба KnownNetworks
а также KnownProxies
Коллекции ForwardedHeadersOptions
или добавить мою сеть докеров 172.17.0.1/16
к списку известных сетей. Сразу после этого у меня наконец-то все заработало.
PS: для тех, кто устанавливает ограничение SSL/TLS на балансировщике нагрузки (например, Amazon Load Balancer или ELB), НЕ устанавливайте заголовок X-Forwarded-Proto
в конфигурации nginx. Это переопределит правильно https
значение, которое пришло от баланса нагрузки к http
Схема и URL перенаправления будут неверными. Я еще не нашел, как просто добавить схему, используемую в nginx, в заголовок вместо того, чтобы переопределять его.
Для пользователей apache необходимо добавить только один заголовок: RequestHeader установить X-Forwarded-Proto "https"
Во-первых, нужно убедиться, что mod_headers включен.