Dockerマシンの外部および内部から Identity Server (STS)に対して認証できるようにしたい。
コンテナーの内部と外部の両方で機能する正しい権限を設定するのに問題があります。権限を内部名mcoidentityserver:5000
に設定すると、APIは認証できますが、クライアントがdockerネットワークの外部にあるため、クライアントはトークンを取得できません。権限を外部名localhost:5000
に設定すると、クライアントはトークンを取得できますが、APIは権限名を認識しません(この場合のlocalhost
はホストマシンであるため)。
権限を何に設定すればよいですか?または、おそらくdocker networkingを調整する必要がありますか?
ASP.NET Core API(Linux)、Identity Server 4(Linux上のASP.NET Core)、PostgreSQLデータベースを使用するWindows 10 Docker開発環境をセットアップしています。 PostgreSQLは問題ではなく、完全を期すために図に含まれています。現時点では、ホスト上でPostgreSQLインスタンスを実行しているため、これは9876にマップされています。 mco
は、当社の略称です。
私は Identity Server 4の指示 に従って起動して実行しています。
Visual Studioでの実行にのみ関連する実行コマンドがあるため、docker-compose.debug.yml
は含めていません。
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
これは、追加の値を注入するためにVisual Studioプラグインによって作成されます。
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
ここで、APIにIdentity Serverを使用して権限を設定するように指示します。
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" }
}
};
}
}
クライアント
コンソールアプリで実行する。
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));
}
前もって感謝します。
IssuerUri
が明示的な定数に設定されていることを確認してください。 IP /ホスト名によるIdentity Serverインスタンスへのアクセスに関して同様の問題があり、次のように解決しました。
services.AddIdentityServer(x =>
{
x.IssuerUri = "my_auth";
})
追伸典拠URLをhostname:5000
に統合してみませんか?はい、Clientと[〜#〜] api [〜#〜]の両方が同じURLを呼び出すことが可能ですhostname:5000
:
hostname:5000
へのアクセス権があります(ファイアウォール、ネットワークトポロジなどを確認してください)DNSは最もトリッキーな部分です。問題が発生した場合は、hostname
を解決するのではなく、公開されたIPでIdentity Serverにアクセスすることをお勧めします。
これを機能させるには、APIが許可されるように、IDサーバーインスタンスでdocker-compose.yml
とセットアップ [〜#〜] cors [〜#〜] の2つの環境変数を渡す必要がありました。それを呼び出す。 CORSの設定は、この質問の範囲外です。 この質問 で十分カバーできます。
IDサーバーにはIDENTITY_ISSUER
が必要です。これは、IDサーバーが自分に付ける名前です。この場合は、Docker HostのIP
とIDサーバーのポートを使用しました。
mcoidentityserver:
image: mcoidentityserver
build:
context: ./Mco.IdentityServer
dockerfile: Dockerfile
environment:
IDENTITY_ISSUER: "http://10.0.75.1:5000"
ports:
- 5000:5000
networks:
- mconetwork
APIは、権限がどこにあるかを知る必要があります。呼び出しはdockerネットワークの外に出る必要がないため、認証にdockerネットワーク名を使用できます。APIはIDサーバーを呼び出してトークンを確認するだけです。
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
Identity Server.cs
ConfigureServices
にIdentity Issuer名を設定します。
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
これで、Authorityを環境変数に設定できます。
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = Configuration["IDENTITY_AUTHORITY"],
RequireHttpsMetadata = false,
ApiName = "api1"
});
ここに示すように、ハードコードされたID発行者はローカルIPであるため、docker-composeは本番環境には適していません。代わりに、IDサーバーが実行されているdockerインスタンスにマップする適切なDNSエントリが必要です。これを行うには、docker-composeオーバーライドファイルを作成し、オーバーライドされた値でプロダクションをビルドします。
ilya-chumakov の協力に感謝します。
さらに、ブログに Linux docker + ASP.NET Core 2 + OAuth with Identity Server を構築するプロセス全体を記述しました。