我已经关注IdentityServer4 quickstarts并且能够使用隐式授权使用IdentityServer的本地主机实例验证我的javascript网页(几乎与快速入门中提供的相同)。同样,我的IdentityServer几乎与上面提到的快速入门中提供的相同 - 它只是有一些自定义用户详细信息。
然后我将我的应用程序(C#.NET Core)移动到一个docker容器中,并在Kubernetes集群(单个实例)中托管了一个这样的实例,并创建了一个Kubernetes服务(一个或多个外观' real&# 39; services),它允许我从集群外部访问身份服务器。我可以修改我的JavaScript网页并将其指向我的Kubernetes服务,它仍然可以很高兴地显示登录页面,它似乎按预期工作。
然后当我将IdentityServer扩展到三个实例(所有服务都在一个Kubernetes服务后面)时,我开始遇到问题。 Kubernetes服务对每个身份服务器进行循环请求,因此第一个将显示登录页面,但第二个将在我按下登录按钮后尝试处理身份验证。这会导致以下错误:
System.InvalidOperationException:防伪令牌不可能 解密。 ---> System.Security.Cryptography.CryptographicException: 密钥{19742e88-9dc6-44a0-9e89-e7b09db83329}未找到 钥匙圈。在 Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector.UnprotectCore(字节[] protectedData,Boolean allowOperationsOnRevokedKeys,UnprotectStatus& 地位) Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector.DangerousUnprotect(字节[] protectedData,Boolean ignoreRevocationErrors,Boolean& requiresMigration,Boolean& was.Revoked)at Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector.Unprotect(字节[] protectedData)at Microsoft.AspNetCore.Antiforgery.Internal.DefaultAntiforgeryTokenSerializer.Deserialize(字符串 serializedToken)---内部异常堆栈跟踪结束--- ......还有更多......
所以 - 我明白我收到了这个错误,因为期望是同一个IdentityServer应该为它所显示的页面提供服务请求(否则反伪造令牌会如何工作,对吧?),但是我我试图理解我是如何在复制环境中完成这项工作的。
我不想在不同的IP /端口上托管多个身份服务器;我正在尝试构建一个HA配置,如果一个IdentityServer死掉,那么任何调用端点都不应该关心(因为请求应该由其他工作实例提供服务)。
我说我正在使用快速入门代码 - 这意味着在IdentityServer的启动过程中,有类似的代码...
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddIdentityServer(options =>
{
options.Events.RaiseSuccessEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseErrorEvents = true;
})
.AddTemporarySigningCredential()
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients())
我假设我需要将.AddTemporarySigningCredential()
逻辑替换为可以由我的Kubernetes集群中运行的IdentityServer的所有实例使用的证书。不知道MVC是如何工作的(MVC6用于生成IdentityServer服务中的登录页面,我从示例代码中获取 - 上面的链接) - 我想知道是否只是更改代码以使用在之间共享的适当证书所有服务都足以让原型HA IdentityServer集群工作吗?
通过工作,我的意思是我的期望是我可以在Kubernetes集群中运行n个IdentityServer实例,使用Kubernetes服务作为我运行的许多IdentityServer的外观,并且是能够使用多个IdentityServer实例进行身份验证,这些实例可以共享数据,以便它们为我的调用Web应用程序提供完全相同的权限,并且可以在一个或多个实例死亡时处理彼此的请求。
任何帮助或见解都将不胜感激。
答案 0 :(得分:5)
我想我已经解决了这个问题。为了解决我的问题,我做了两件事:
services.AddIdentityServer(...).AddSigningCredential(new X509Certificate2(bytes, "password")
。事实证明有一个Redis backed KSP available from NuGet,这意味着我只需要在我的Kube集群中启动一个私有redis实例(在我的集群之外无法访问)以共享解密秘密
/* Note: Use an IP, or resolve from DNS prior to adding redis based key store as direct DNS resolution doesn't work for this inside a K8s cluster, though it works quite happily in a Windows environment. */
var redis = ConnectionMultiplexer.Connect("1.2.3.4:6379");
services.AddDataProtection()
.PersistKeysToRedis(redis, "DataProtection-Keys");
我现在可以将我的身份服务扩展到3个实例,并让Kube服务充当所有可用实例的外观。我可以在身份服务之间观看日志作为Kubernetes循环请求,我的身份验证就像我期望的那样发生。
感谢那些在这篇文章之前评论过这个问题的人。
答案 1 :(得分:0)
对于使用Kubernetes的用户,可以使用文件系统密钥存储提供程序
public void ConfigureServices(IServiceCollection services)
{
services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(@"/app/key-storage"));
}
其中目录'/ app / key-storage'映射到nfs支持的持久卷。
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-key-storage
spec:
selector:
matchLabels:
type: nfs-pv
storageClassName: manual
accessModes:
- ReadWriteMany
resources:
requests:
storage: 10Mi
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv
labels:
type: nfs-pv
spec:
storageClassName: manual
capacity:
storage: 10Mi
accessModes:
- ReadWriteMany
nfs:
server: <server>
path: /<path>
persistentVolumeReclaimPolicy: Delete
在IDP部署中
template:
spec:
containers:
- name: <name>
volumeMounts:
- name: key-storage
mountPath: /app/key-storage
readOnly: false
volumes:
- name: key-storage
persistentVolumeClaim:
claimName: pvc-key-storage
您需要签名证书。可以将其添加为机密,然后IDP部署可以使用另一个卷来安装机密(未显示)。
apiVersion: v1
kind: Secret
metadata:
name: cert-secret
labels:
app: <app-label>
type: Opaque
data:
signingcert.pfx: <base64 cert value>