Java GSS-API服务票证未保存在使用Java的凭证高速缓存中
我创建了2个使用GSS-API的演示Kerberos客户端。 一个在Python3中,第二个在Java中。 这两个客户端似乎大致相同,并且都“工作”,因为我得到的服务票据被我的Java GSS-API服务负责人接受。Java GSS-API服务票证未保存在使用Java的凭证高速缓存中
但是在测试中,我注意到Python客户端将服务票证保存在kerberos凭证缓存中,而Java客户端似乎不保存票证。
我使用“klist”来查看凭证缓存的内容。
我的客户端运行在Lubuntu 17.04虚拟机上,使用FreeIPA作为Kerberos环境。我正在使用OpenJDK 8 u131。
问题1: Java GSS-API是否不将服务票据保存到凭证缓存?或者我可以更改我的代码吗?
问题2:服务票证未保存到缓存中是否有任何缺点?
我的假设是缓存的服务票据减少了与KDC的交互,但对How to save Kerberos Service Ticket using a Windows Java client?的评论建议不是这种情况,但this Microsoft technote说“客户端不需要返回到KDC每次它想要访问此特定服务器“。
问题3:来自python客户端的缓存服务票据在到期日前的几分钟之前就会消失。什么导致他们消失?
Python代码
#!/usr/bin/python3.5
import gssapi
from io import BytesIO
server_name = 'HTTP/[email protected]'
service_name = gssapi.Name(server_name)
client_ctx = gssapi.SecurityContext(name=service_name, usage='initiate')
initial_client_token = client_ctx.step()
的Java代码
System.setProperty("java.security.krb5.conf","/etc/krb5.conf");
System.setProperty("javax.security.auth.useSubjectCredsOnly","false");
GSSManager manager = GSSManager.getInstance();
GSSName clientName;
GSSContext context = null;
//try catch removed for brevity
GSSName serverName =
manager.createName("HTTP/ap[email protected]", null);
Oid krb5Oid = new Oid("1.2.840.113554.1.2.2");
//use default credentials
context = manager.createContext(serverName,
krb5Oid,
null,
GSSContext.DEFAULT_LIFETIME);
context.requestMutualAuth(false);
context.requestConf(false);
context.requestInteg(true);
byte[] token = new byte[0];
token = context.initSecContext(token, 0, token.length);
编辑:
虽然原来的问题主要论点集中在使用Java GSS-API来构建一个Java Kerberos客户端,GSS不是很重要吨。我愿意接受适用于Java的其他Kerberos方法。现在我正在试验Apache Kerby curb-client。
到目前为止的Java GSS-API似乎有2个问题:
1)它使用凭证高速缓存,以获得TGT(OK),但不缓存服务门票(不正常)。
2)它不能访问KEYRING类型的凭证缓存。 (通过行为,调试Java运行时安全类以及该代码中的注释来确认对于Lubuntu/FreeIPA组合,我使用的是KEYRING,它是开箱即用的默认设置,它不适用于Windows,并且可能不适用于其他Linux Kerberos组合。
编辑2:
我应该问的问题是:
如何阻止我的KDC从因为Java GSS没有使用凭证高速缓存敲定重复SGT请求。
我在底部留下我的原始答案,因为如果主要集中在原始问题。
经过又一轮的深度调试和测试,我发现了一个可接受的根本问题解决方案。
使用Java GSS API与JAAS,而不是无JAAS“纯净” GSS在我原来的解决方案产生很大的差别!
是,现有的服务票据(辅导教师),可能是在凭证高速缓存没有被加载, 也不是写回缓存任何新收购的学生辅导教师,但是KDC不是不断地敲打(实问题)。
纯粹的GSS和带有JAAS的GSS都使用客户端主体。主题有一个内存中的私人凭据集 ,用于存储TGT和SGT。
的主要区别是:
“纯GSS”:主题+ privateCredentials是GSSContext中创建的,而且只要仅作为GSSContext住住。
GSS与JAAS:主体由JAAS创建的GSSContext外部,因此能活在应用程序的生命, 应用程序的生命周期内涵盖多种GSSContexts。
建立将查询对象的privateCredentials的SGT,找不到一个第一GSSContext, 然后请求从KDC一个SGT。
SGT被添加到主题的privateCredentials中,并且由于主题的生命周期长于GSSContext,所以在创建下面的GSSContexts时它是可用的,就像SGT一样。他们会在该主题的私人知识产权中找到SGT,并且不需要为KDC创建新的SGT。
因此,从我的特定Java胖客户端的角度看,一次打开并可能运行几个小时,一切都很好。 创建的第一个GSSContext将触发一个SGT的KDC,然后这个SGT将被所有的GSSContext创建,直到客户端关闭。 凭证缓存没有被使用,但这并没有伤害。
鉴于客户端寿命短得多,重新打开很多次,也许并行,然后使用/不使用凭证缓存可能是一个更严重的问题。
private void initJAASandGSS() {
LoginContext loginContext = null;
TextCallbackHandler cbHandler = new TextCallbackHandler();
try {
loginContext = new LoginContext("wSOXClientGSSJAASLogin", cbHandler);
loginContext.login();
mySubject = loginContext.getSubject();
} catch (LoginException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
gssManager = GSSManager.getInstance();
try {
//TODO: LAMB: This name should be got from config/built from config/serviceIdentifier
serverName = gssManager.createName("HTTP/[email protected]", null);
Oid krb5Oid = new Oid("1.2.840.113554.1.2.2");
} catch (GSSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private String getGSSwJAASServiceToken() {
byte[] token = null;
String encodedToken = null;
token = Subject.doAs(mySubject, new PrivilegedAction<byte[]>(){
public byte[] run(){
try{
System.setProperty("javax.security.auth.useSubjectCredsOnly","true");
GSSContext context = gssManager.createContext(serverName,
krb5Oid,
null,
GSSContext.DEFAULT_LIFETIME);
context.requestMutualAuth(false);
context.requestConf(false);
context.requestInteg(true);
byte[] ret = new byte[0];
ret = context.initSecContext(ret, 0, ret.length);
context.dispose();
return ret;
} catch(Exception e){
Log.log(Log.ERROR, e);
throw new otms.util.OTMSRuntimeException("Start Client (Kerberos) failed, cause: " + e.getMessage());
}
}
});
encodedToken = Base64.getEncoder().encodeToString(token);
return encodedToken;
}
结束编辑2:
问题1:是否在Java GSS-API不服务票证保存凭证高速缓存以下原来的答复?或者我可以更改我的代码吗?
编辑:根本原因分析。 。
多小时的调试后sun.security *类,我现在明白GSS和Java安全代码是做/不这样做 - 至少在Java中8ü131
在这个例子中,我们有一个凭证缓存,Java GSS可以访问的类型,包含有效的票据授予票据(TGT)和有效的服务票据(SGT)。
1)当创建客户主体主体时,TGT从缓存(Credentials.acquireTGTFromCache())中加载并存储在主体的privateCredentials集中。 - >(OK)
只有TGT被加载,SGT才会被加载并保存到主体privateCredentials中。 - >(NOT OK)
2)后来,深入GSSContext.initSecContext()过程中,安全代码实际上试图从Subject的privateCredentials中检索Service Ticket。相关的代码是Krb5Context.initSecContext()/ KrbUtils.getTicket()/ SubjectComber.find()/ findAux()。但是,由于SGT从未在步骤1中加载),因此不会找到SGT!因此要求KDC使用新的SGT。
这对每个服务请求都重复执行。
为了好玩,并且严格地将其作为概念证明破解,我在登录和initSecContext()之间添加了几行代码以解析凭证缓存,提取凭证,转换为Krb凭证,并将它们添加到主题的私人凭证。
这样做,在步骤2)中找到并使用了现有的SGT。 KDC没有要求新的SGT。
我不会发布此黑客的代码,因为它会调用我们不应该调用的sun内部类,而且我不希望启发任何其他人这样做。我也不打算将这种黑客作为解决方案。
- >根本原因问题不是服务票据不是SAVED到缓存;而是
a)在辅导教师不受压从凭据缓存到客户端主要
和
B的主题),没有公开的API或配置设置这样做。
这会影响GSS-API使用和不使用JAAS。
那么,这使我离开了哪里?i)使用带有JAAS的Java GSS-API/GSS-API“按原样”,每个SGT请求触及KDC - >不好。 ii)如Samson在下面的评论中所建议的那样,仅对应用程序的初始登录使用Java GSS-API,然后对于所有进一步的调用,为后续调用使用替代的安全机制(一种自建的kerberos-光)使用令牌或饼干。
iii)考虑GSS-API的替代方案,如Apache Kerby curb-client。这在这个答案的范围之外有意义,并且很可能证明是从谚语的煎锅跳到火中。
我已经向Oracle提交了一个Java Feature Request,建议应该从缓存中检索SGT并将其存储在Subject凭证中(正如TGT的情况一样)。
http://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8180144
问题2:有什么缺点的事实,维护票据没有保存到缓存?
使用服务凭证的凭证缓存可减少客户端与KDC之间的交互。由此推论,如果服务票未被缓存,每个请求都需要与KDC进行交互,这可能会导致KDC受到攻击。
Hadoop生态系统使用服务票据_just once_生成值得生成的值某种“身份验证令牌” - 例如用于RPC通信的MD5哈希,或者用于REST和GUI的签名cookie--直到它过期并需要另一轮Kerberos身份验证。官方原因是Kerberos不是为分布式系统设计的,所以在N个主机上进行1项服务的验证非常麻烦。但是现在我认为,缺乏对高速缓存服务票据的Java支持也是一种很好的激励措施。 –
问你的问题,_“使用服务票据缓存减少客户端和KDC之间的交互”_ >>它总是与“客户端”相同,即服务,还是简单地按顺序运行多个呼叫的应用程序?如果是的话,拥有一个*共享*缓存的好处是什么,而不是简单地管理一个私有的'context'对象集合? –
您的代码与@FlyingSheep中的代码错误。你永远不会完成auth循环。伙计们,不要在工作中尝试这个。 –
btw:我使用的是没有JAAS的GSS – FlyingSheep
Windows或Linux?它制造了巨大的差异。 –
@SamsonScharfrichter目前的Linux。正如我在问题中所说的,客户端运行于** Lubuntu 17.04 **。为了让Java GSS-API使用凭证缓存,我必须从标准KEYRING切换到FILE。 Python GSS对KEYRING很满意。 – FlyingSheep