Para empezar, tenemos que configurar el servicio de Identity Server a través de clase IdentityServerOptions en nuestro método ConfigureServices.
services.AddIdentityServer(options => { options.PublicOrigin = "https://myurl.loadbalancer.com"; options.IssuerUri = "https://myurl.loadbalancer.com"; }) .AddOperationalStore(options =>; options.ConfigureDbContext = builder => builder.UseSqlServer(connectionString, sqlOptions => sqlOptions.MigrationsAssembly(migrationsAssembly))) .AddConfigurationStore(options => options.ConfigureDbContext = builder => builder.UseSqlServer(connectionString, sqlOptions => sqlOptions.MigrationsAssembly(migrationsAssembly))) .AddAspNetIdentity<ApplicationUser>() .AddDeveloperSigningCredential();
Veamos en detalle las dos opciones que hemos establecido:
IssuerUri
PublicOrigin
Con ésto, si iniciamos nuestro Identity Server y nos posicionamos en la siguiente URL/ .well-known/openid-configuration, se mostrará la nueva configuración.
Como se puede observar, tanto el issuer como las URLs de los endpoints expuestos por Identity Server son diferentes a la URL del host (en este caso https://localhost:44302).
Continuando con la configuración de Identity Server vamos a explicar ahora en que consisten las cabeceras X-Forwarded-*.
El formato de esta cabecera es el siguiente
El formato de esta cabecera es el siguiente
El formato de esta cabecera es el siguiente
El formato de esta cabecera es el siguiente
Ya conocemos las cabeceras que debe exponer el balanceador de carga o el proxy. La siguiente configuración aplica tanto a nuestro Identity Server como a nuestras aplicaciones web que van a autenticarse contra él y también están detrás del balanceador.
En nuestro método ConfigureServices aplicamos la siguiente configuración (al inicio del método o antes de .AddIdentityServer)
public IServiceProvider ConfigureServices(IServiceCollection services) { services.Configure<ForwardedHeadersOptions>(options => { options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; }); … services.AddIdentityServer(options => { options.PublicOrigin = "https://myurl.loadbalancer.com"; options.IssuerUri = "https://myurl.loadbalancer.com"; }) .AddOperationalStore(options => options.ConfigureDbContext = builder => builder.UseSqlServer(connectionString, sqlOptions => sqlOptions.MigrationsAssembly(migrationsAssembly))) .AddConfigurationStore(options => options.ConfigureDbContext = builder => builder.UseSqlServer(connectionString, sqlOptions => sqlOptions.MigrationsAssembly(migrationsAssembly))) .AddAspNetIdentity<ApplicationUser>() .AddDeveloperSigningCredential(); … }
En nuestro método Configure aplicamos la siguiente configuración (al inicio del método o antes de .UseIdentityServer)
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { var fordwardedHeaderOptions = new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto }; fordwardedHeaderOptions.KnownNetworks.Clear(); fordwardedHeaderOptions.KnownProxies.Clear(); app.UseForwardedHeaders(fordwardedHeaderOptions); … app.UseIdentityServer(); … }
Bien, ya tenemos configurado nuestro Identity Server para colocarlo detrás de cualquier balanceador de carga o proxy … ¿o no?
Lo siguiente que vamos a contar es fruto de una lucha con Azure Api Gateway en la que nos hemos enfrascado hace poco.
Azure Api Gateway mola mucho, pero tiene un pequeño gran inconveniente, NO genera la cabecera X-Forwarded-Host, sino que devuelve una cabecera X-ORIGINAL-HOST (ver Add the X-Forwarded-Host header to Application Gateway), por lo que cuando se configura una aplicación web para que autentique usuarios vía Identity Server, a la hora de generar la URL de redirección, ésta se generaba de la siguiente forma:
https://myurl.loadbalancer.com/connect/authorize? client_id=myClientMVC& redirect_uri=https://10.124.1.4/signin-oidc& response_type=code id_token& scope=openid profile& response_mode=form_post& nonce=636801444519810075.MTcwNjg1YzctNTAyZC00NDkxLWIxM2EtOWVhMGUzM2U4ODgzYmYzMGY1NDEtZDE0Yy00YTFkLWJhMjMtODhiMWNmNDQwMzdm&state=CfDJ8O7U_mOnuzpOo4VdJnNW0qCKkrd5eDfkxIO1GzUdULyVoiSQfIXkVst6PdpilljuA8KBh2kBROZ3hoG_K8i1V-fgIjcRs23mHuqbowRnd77dKzE8cZRt0VJuvE1Rns8jPcPLUXrCnj76TekCtZshgXw8HiL6dUSWSl2CqigtxBhcZY0p70m_2vYe80xORrpcc59MFsrAlX94z_UkNsacUsEmFbzKvgHVLS5VKT677gp7R284N6Q6Y6Ty15CDzXN1XQgqGxprVp3CDDWS7JaPKxXs5puyqgYHTjeuOVrqNFhJhi4ErLjplej9TNrBVfwtbQ&x-client-SKU=ID_NET&x-client-ver=2.1.4.0
Si os fijáis, la URL de redirección es la IP del host de la aplicación web. ¿Cómo solventar este problema? Pues es muuuuuy sencillo. Aprovechando que Asp.Net Core todo son Middlewares, simplemente después de configurar las cabeceras X-Forwarded-* se aplica el siguiente truquito.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { var fordwardedHeaderOptions = new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto | ForwardedHeaders.XForwardedHost }; fordwardedHeaderOptions.KnownNetworks.Clear(); fordwardedHeaderOptions.KnownProxies.Clear(); app.UseForwardedHeaders(fordwardedHeaderOptions); string XOriginalHost = "X-ORIGINAL-HOST"; app.Use((context, next) => { if (context.Request.Headers.TryGetValue(XOriginalHost, out StringValues originalHostName)) { context.Request.Host = new HostString(originalHostName); } return next(); }); … }
En el middleware, se analiza la cabecera de la petición y, si existe la cabecera X-ORIGINAL-HOST, se sobrescribe en la petición. Fijaos también que hemos modificado las ForwardedHeadersOptions para recuperar la cabecera X-Forwarded-Host.
Ahora sí que sí ¡lo tenemos!
Como colofón, os voy a explicar cómo configurar Identity Server en el caso en que se requiera aplicar SSL Offload (básicamente es que toda comunicación HTTPS se realiza hasta al balanceador de carga y a partir de ahí, la comunicación con las aplicaciones web se realiza vía HTTP)
Para ello se configura el pipeline de ejecución de nuestra aplicación web o de nuestro Identity Server (se requiere .Net Core 2.1 o posterior)
public IServiceProvider ConfigureServices(IServiceCollection services) { services.Configure<ForwardedHeadersOptions>(options => { options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto | ForwardedHeaders.XForwardedHost; }); services.AddHsts(options => { options.Preload = true; options.IncludeSubDomains = true; options.MaxAge = TimeSpan.FromDays(60); }); services.AddHttpsRedirection(options => options.RedirectStatusCode = StatusCodes.Status307TemporaryRedirect); … } public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { var fordwardedHeaderOptions = new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto | ForwardedHeaders.XForwardedHost }; fordwardedHeaderOptions.KnownNetworks.Clear(); fordwardedHeaderOptions.KnownProxies.Clear(); app.UseForwardedHeaders(fordwardedHeaderOptions); string XOriginalHost = "X-ORIGINAL-HOST"; app.Use((context, next) => { if (context.Request.Headers.TryGetValue(XOriginalHost, out StringValues originalHostName)) { context.Request.Host = new HostString(originalHostName); } return next(); }); app.UseHttpsRedirection(); … }
En este POST hemos visto que podemos configurar nuestro Identity Server y aplicaciones web en un entorno productivo que se encuentren detrás de un balanceador de carga o proxy. También hemos repasado cómo aplicar el SSL Offload a nuestro Identity Server y algún que otro truquito para Azure Api Gateway…así que, HAPPY CODING!
Este sitio web utiliza cookies para que tengas la mejor experiencia de usuario. Si continuas navegando, estás dando tu consentimiento para aceptar las cookies y también nuestra política de cookies (esperemos que no te empaches con tanta cookie 😊)