我使用GSS创建了2个Kerberos演示客户端。一个在Python3,第二个在Java中。这两个客户端似乎大致等同,而且都是“工作”的,因为我得到了一个被我的Java服务主体接受的服务票。
但是,在测试中,我注意到Python客户端将服务票证保存在kerberos凭据缓存中,而Java客户端似乎没有保存票证。
我使用"klist“来查看凭证缓存的内容。
我的客户端运行在一个Lubuntu17.04虚拟机上,使用FreeIPA作为Kerberos环境。我正在使用OpenJDK 8 u131。
问题1: Java是否没有将服务票保存到凭据缓存?或者我可以修改我的代码,这样它就这样做了吗?
问题2:没有将服务票证保存到缓存中,这有什么缺点吗?
我的假设是,缓存的服务票减少了与KDC的交互,但如何使用Windows客户端保存Kerberos服务票证?上的评论表明情况并非如此,但这个微软technote说,“每次客户机想要访问这个特定的服务器时,都不需要返回到KDC”。
问题3:从python缓存的服务票在几分钟后消失--在到期日期之前很久。是什么让他们消失的?
Python代码
#!/usr/bin/python3.5
import gssapi
from io import BytesIO
server_name = 'HTTP/app-srv.acme.com@ACME.COM'
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/app-srv.acme.com@ACME.COM", 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并不是必须的。我对其他在Java上工作的Kerberos方法持开放态度。现在,我正在试验Apache客户端。
到目前为止,Java似乎存在两个问题:
1)它使用凭证缓存获取TGT ( Ok),但不缓存服务票(Not )。
2)不能访问密钥环类型的凭证缓存。(由行为、调试Java运行时安全类以及代码中的注释确认。对于Lubuntu / FreeIPA组合,我使用的KEYRING是开箱即用的默认值。这不适用于Windows,也可能不适用于其他Linux组合。
发布于 2017-05-07 19:13:58
编辑2:
我应该问的问题是:
由于Java没有使用凭据缓存,我如何防止重复的SGT请求对我的KDC造成影响。
我把原来的答案放在最下面,因为如果主要集中在原来的问题上。
经过另一轮的深入调试和测试,我找到了一个可以接受的解决方案。
在我最初的解决方案中使用JAAS和JAAS,而不是没有JAAS的“纯”,这就产生了很大的不同!
是的,可能存在于凭据缓存中的现有服务票证(SGT)没有被加载,也没有任何新获得的SGT写入缓存,但是KDC没有不断地被锤击(真正的问题)。
纯GSS和带有JAAS的GSS都使用客户端主体。该主题有一个内存中的privateCredentials集,用于存储TGT和SGT.
关键的区别是:
建立的第一个GSSContext将查询主题的privateCredentials以获得一个SGT,而不是找到一个,然后从KDC请求一个SGT。
SGT被添加到主题的privateCredentials中,并且由于主题的生存期比GSSContext长,所以在创建GSSContexts之后,它是可用的,就像SGT一样。这些将在主题的privateCredentials中找到SGT,并且不需要为新的SGT点击KDC。
因此,根据我特定的Java客户机,打开一次并可能运行几个小时,一切都很好。创建的第一个GSSContext将命中SGT的KDC,然后所有后续创建的GSSContexts都将使用它,直到客户机关闭为止。没有使用凭据缓存,但这并不有害。
鉴于一个活动时间短得多的客户端,多次重新打开,并且可能是并行的,那么使用/不使用凭据缓存可能是一个更严重的问题。
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/app-srv.acme.com@ACME.COM", 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是否没有将服务票保存到凭据缓存?或者我可以修改我的代码,这样它就这样做了吗?
编辑:根本原因分析。
在调试sun.security.*类很多小时之后,我现在了解了GSS和sun.security代码正在做/不做什么--至少在Java 8u 131中是这样的。
在本例中,我们有一个凭证缓存,它是Java可以访问的类型,包含有效的票证授予票(TGT)和有效的服务票证(SGT)。
1)创建客户机主体时,从缓存(Credentials.acquireTGTFromCache())加载TGT,并将其存储在主题的privateCredentials集中。-> (OK)
只有TGT被加载,TGT没有加载并保存到主题privateCredentials中。->(不确定)
2)稍后,在GSSContext.initSecContext()流程的深处,安全代码实际上试图从主题的privateCredentials中检索服务票证。相关代码是Krb5Context.initSecContext() / KrbUtils.getTicket() / SubjectComber.find()/findAux()。但是,由于SGT从未在步骤1中加载过,因此不会找到SGT!因此,向KDC请求并使用一个新的SGT。
对于每个服务请求,都会重复这种情况。
为了好玩,严格地作为概念黑客的证明,我在登录和initSecContext()之间添加了几行代码来解析凭证缓存、提取凭据、转换为Krb凭据,并将它们添加到主题的私有凭据中。
在步骤2中,找到并使用现有的SGT。没有新中士被要求从KDC。
我不会发布这个黑客的代码,因为它调用我们不应该调用的sun内部类,而且我不希望激励其他任何人这样做。我也不打算用这个黑客作为解决方案。
->根本原因不是没有将服务票证保存到缓存中,而是因为服务票没有保存到缓存中;
a) SGT没有从凭据缓存加载到客户机主体的主题
和
( b)没有公共API或配置设置这样做。
这会影响GSS的使用和不使用JAAS。
那我该怎么办呢?
(1)在JAAS中使用/GSS,每个SGT请求都会碰到KDC ->不太好。
( ii)正如Samson在下面的注释中所建议的,只在应用程序的初始登录时使用Java,然后对于所有进一步的调用,使用令牌或cookie对后续调用(一种自行构建的kerberos-light)使用另一种安全机制。
(三)考虑GSS的替代方案,如.这意味着超出了这个答案的范围,而且很可能被证明是从平底锅跳到火上。
我向Oracle提交了一个Java特性请求,建议从缓存中检索SGT并将其存储在主题凭据中(就像TGT的情况一样)。
id=JDK-8180144
问题2:没有将服务票证保存到缓存中,这有什么缺点吗?
使用服务票证的凭据缓存可以减少客户端与KDC之间的交互。由此产生的结果是,在没有缓存服务票的情况下,每个请求都需要与KDC进行交互,这可能导致KDC被锤击。
https://stackoverflow.com/questions/43786908
复制相似问题