test hub examen compose and docker docker-compose identityserver4

examen - github docker hub



¿Cómo puedo usar IdentityServer4 desde dentro y fuera de una máquina de conexión? (2)

Quiero poder autenticarme contra un servidor de identidad (STS) desde fuera y dentro de una máquina de conexión.

Estoy teniendo problemas para establecer la autoridad correcta que funcione tanto dentro como fuera del contenedor. Si configuro la autoridad en el nombre interno mcoidentityserver:5000 entonces la API puede autenticarse, pero el cliente no puede obtener un token ya que el cliente se encuentra fuera de la red de la ventana acoplable. Si configuro la autoridad en el nombre externo localhost:5000 entonces el cliente puede obtener un token pero la API no reconoce el nombre de la autoridad (porque localhost en este caso es la máquina host).

¿Qué debo establecer la Autoridad para? O tal vez necesito ajustar la red docker?

Diagrama

La flecha roja es la parte con la que estoy teniendo problemas.

Detalle

Estoy configurando un entorno de desarrollo docker de Windows 10 que utiliza una API de ASP.NET Core (en Linux), Identity Server 4 (ASP.NET Core en Linux) y una base de datos PostgreSQL. PostgreSQL no es un problema, incluido en el diagrama para completar. Se asigna a 9876 porque también tengo una instancia de PostgreSQL que se ejecuta en el host por ahora. mco es un nombre abreviado de nuestra empresa.

He estado siguiendo las instrucciones de Identity Server 4 para empezar a funcionar.

Código

No estoy incluyendo el docker-compose.debug.yml porque ha ejecutado los comandos pertinentes solo para ejecutarse en Visual Studio.

docker-compose.yml

version: ''2'' services: mcodatabase: image: mcodatabase build: context: ./Data dockerfile: Dockerfile restart: always ports: - 9876:5432 environment: POSTGRES_USER: mcodevuser POSTGRES_PASSWORD: password POSTGRES_DB: mcodev volumes: - postgresdata:/var/lib/postgresql/data networks: - mconetwork mcoidentityserver: image: mcoidentityserver build: context: ./Mco.IdentityServer dockerfile: Dockerfile ports: - 5000:5000 networks: - mconetwork mcoapi: image: mcoapi build: context: ./Mco.Api dockerfile: Dockerfile ports: - 56107:80 links: - mcodatabase depends_on: - "mcodatabase" - "mcoidentityserver" networks: - mconetwork volumes: postgresdata: networks: mconetwork: driver: bridge

docker-compose.override.yml

Esto es creado por el complemento de Visual Studio para inyectar valores extra.

version: ''2'' services: mcoapi: environment: - ASPNETCORE_ENVIRONMENT=Development ports: - "80" mcoidentityserver: environment: - ASPNETCORE_ENVIRONMENT=Development ports: - "5000"

API Dockerfile

FROM microsoft/aspnetcore:1.1 ARG source WORKDIR /app EXPOSE 80 COPY ${source:-obj/Docker/publish} . ENTRYPOINT ["dotnet", "Mco.Api.dll"]

Identity Server Dockerfile

FROM microsoft/aspnetcore:1.1 ARG source WORKDIR /app COPY ${source:-obj/Docker/publish} . EXPOSE 5000 ENV ASPNETCORE_URLS http://*:5000 ENTRYPOINT ["dotnet", "Mco.IdentityServer.dll"]

API Startup.cs

Donde le decimos a la API que use el servidor de identidad y establezca la autoridad.

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(Configuration.GetSection("Logging")); loggerFactory.AddDebug(); app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions { // This can''t work because we''re running in docker and it doesn''t understand what localhost:5000 is! Authority = "http://localhost:5000", RequireHttpsMetadata = false, ApiName = "api1" }); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); } app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); }

Identity Server Startup.cs

public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddIdentityServer() .AddTemporarySigningCredential() .AddInMemoryApiResources(Config.GetApiResources()) .AddInMemoryClients(Config.GetClients()); } public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseIdentityServer(); app.Run(async (context) => { await context.Response.WriteAsync("Hello World!"); }); } }

Identity Server Config.cs

public class Config { public static IEnumerable<ApiResource> GetApiResources() { return new List<ApiResource> { new ApiResource("api1", "My API") }; } public static IEnumerable<Client> GetClients() { return new List<Client> { new Client { ClientId = "client", // no interactive user, use the clientid/secret for authentication AllowedGrantTypes = GrantTypes.ClientCredentials, // secret for authentication ClientSecrets = { new Secret("secret".Sha256()) }, // scopes that client has access to AllowedScopes = { "api1" } } }; } }

Cliente

Ejecutando en una aplicación de consola.

var discovery = DiscoveryClient.GetAsync("localhost:5000").Result; var tokenClient = new TokenClient(discovery.TokenEndpoint, "client", "secret"); var tokenResponse = tokenClient.RequestClientCredentialsAsync("api1").Result; if (tokenResponse.IsError) { Console.WriteLine(tokenResponse.Error); return 1; } var client = new HttpClient(); client.SetBearerToken(tokenResponse.AccessToken); var response = client.GetAsync("http://localhost:56107/test").Result; if (!response.IsSuccessStatusCode) { Console.WriteLine(response.StatusCode); } else { var content = response.Content.ReadAsStringAsync().Result; Console.WriteLine(JArray.Parse(content)); }

Gracias por adelantado.


Asegúrese de que IssuerUri se establece en una constante explícita. Tuvimos problemas similares con el acceso a la instancia de Identity Server por el IP / nombre de host y lo resolvimos de esta manera:

services.AddIdentityServer(x => { x.IssuerUri = "my_auth"; })

PS ¿Por qué no unifica la URL de autoridad al hostname:5000 de hostname:5000 ? Sí, es posible que el Cliente y la API llamen al mismo URL hostname:5000 si:

  • El puerto 5000 está expuesto (veo que está bien)
  • DNS se resuelve dentro del contenedor docker.
  • Tiene acceso al hostname:5000 de hostname:5000 (verifique firewalls, topología de red, etc.)

DNS es la parte más difícil. Si tiene algún problema con él, le recomiendo que intente llegar a Identity Server por su IP expuesta en lugar de resolver el hostname de hostname .


Para hacer que esto funcionara, necesitaba pasar dos variables de entorno en el docker-compose.yml y configurar CORS en la instancia del servidor de identidad para que la API pudiera llamarla. La configuración de CORS está fuera del alcance de esta pregunta; esta pregunta lo cubre bien.

Docker-Componer cambios

El servidor de identidad necesita IDENTITY_ISSUER , que es el nombre que el servidor de identidad se dará a sí mismo. En este caso, he utilizado la IP del IP de la ventana acoplable y el puerto del servidor de identidad.

mcoidentityserver: image: mcoidentityserver build: context: ./Mco.IdentityServer dockerfile: Dockerfile environment: IDENTITY_ISSUER: "http://10.0.75.1:5000" ports: - 5000:5000 networks: - mconetwork

La API necesita saber dónde está la autoridad. Podemos usar el nombre de la red de la ventana acoplable para la autoridad porque la llamada no necesita salir de la red de la ventana acoplable, la API solo está llamando al servidor de identidad para verificar el token.

mcoapi: image: mcoapi build: context: ./Mco.Api dockerfile: Dockerfile environment: IDENTITY_AUTHORITY: "http://mcoidentityserver:5000" ports: - 56107:80 links: - mcodatabase - mcoidentityserver depends_on: - "mcodatabase" - "mcoidentityserver" networks: - mconetwork

Usando estos valores en C #

Identity Server.cs

Establece el nombre del Emisor de Identidad en ConfigureServices :

public void ConfigureServices(IServiceCollection services) { var sqlConnectionString = Configuration.GetConnectionString("DefaultConnection"); services .AddSingleton(Configuration) .AddMcoCore(sqlConnectionString) .AddIdentityServer(x => x.IssuerUri = Configuration["IDENTITY_ISSUER"]) .AddTemporarySigningCredential() .AddInMemoryApiResources(Config.GetApiResources()) .AddInMemoryClients(Config.GetClients()) .AddCorsPolicyService<InMemoryCorsPolicyService>() .AddAspNetIdentity<User>(); }

API Startup.cs

Ahora podemos establecer la Autoridad a la variable de entorno.

app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions { Authority = Configuration["IDENTITY_AUTHORITY"], RequireHttpsMetadata = false, ApiName = "api1" });

Inconvenientes

Como se muestra aquí, la compilación de la ventana acoplable no sería adecuada para la producción, ya que el emisor de identidad codificado es una IP local. En su lugar, necesitaría una entrada de DNS adecuada que se asignaría a la instancia de la ventana acoplable con el servidor de identidad ejecutándose en ella. Para hacer esto, crearía un archivo de reemplazo de compilación acoplable y compilaría la producción con el valor reemplazado.

Gracias a por su ayuda.

Editar

Además de esto, escribí el proceso completo de construcción de una ventana acoplable Linux + ASP.NET Core 2 + OAuth con Identity Server en mi blog.