我已经使用GSS-API创建了2个演示Kerberos客户端。
一个在Python3中,第二个在Java中。
这两个客户端似乎大致相同,并且两者都“起作用”,因为我得到了我的Java GSS-API服务主体所接受的服务票证。
但是在测试中,我注意到Python客户端将服务票证保存在kerberos凭证缓存中,而Java客户端似乎没有保存票证。
我使用“klist”来查看凭证缓存的内容。
我的客户端使用FreeIPA作为Kerberos环境,在Lubuntu 17.04虚拟机上运行。我正在使用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/[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 Client上,但GSS不是必须的。我对在Java上工作的其他Kerberos方法持开放态度。现在,我正在尝试使用Apache Kerby kerb-client。
到目前为止,Java GSS-API似乎有两个问题:
1)它使用凭据缓存获取TGT(确定),但不缓存服务凭单(不确定)。
2)它无法访问KEYRING类型的凭据缓存。 (由行为,调试Java运行时安全性类以及该代码中的注释确认。对于Lubuntu / FreeIPA组合,我使用的是KEYRING的默认值。这不适用于Windows,可能适用于Windows不适用于其他Linux Kerberos组合。
最佳答案
编辑2:
我应该问的问题是:
由于Java GSS未使用凭据缓存,因此如何阻止我的KDC重复SGT请求。
我将原始答案留在最下面,因为如果主要集中在原始问题上。
经过另一轮深入的调试和测试,我找到了可以解决根本问题的解决方案。
在我的原始解决方案中,将Java GSS API与JAAS结合使用,与不带JAAS的“纯” GSS相比有很大的不同!
是的,凭证缓存中可能存在的现有服务票证(SGT)未加载,
也不会将任何新获取的SGT写回到缓存中,但是KDC不会经常受到冲击(真正的问题)。
纯GSS和带有JAAS的GSS都使用客户主体。该主题有一个内存中的privateCredentials集合,
用于存储TGT和SGT。
关键区别在于:
在应用程序的生命周期内跨越许多GSSContext。
建立的第一个GSSContext将查询主题的privateCredentials以获取SGT,但找不到一个,
然后向KDC请求SGT。
SGT被添加到主题的privateCredentials中,并且随着主题的生存期超过GSSContext,
创建以下GSSContext时,它与SGT一样可用。这些将在主题的privateCredentials中找到SGT,而无需点击KDC即可获得新的SGT。
因此,从我的特定Java Fat Client(一次打开并且可能运行数小时)的角度来看,一切正常。
创建的第一个GSSContext将命中SDC的KDC,然后由所有随后创建的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 Security代码在做什么/不做什么-至少在Java 8 u 131中。
在此示例中,我们具有Java GSS可以访问的凭证高速缓存,其中包含有效的票证授予票证(TGT)和有效的服务票证(SGT)。
1)创建客户端主体Subject时,将从缓存(Credentials.acquireTGTFromCache())加载TGT,并将其存储在Subject的privateCredentials集中。 ->(确定)
仅加载TGT,不加载SGT并将其保存到主题privateCredentials。 ->(不正确)
2)稍后,在GSSContext.initSecContext()过程的深处,安全代码实际上尝试从主题的privateCredentials检索服务票证。相关代码是Krb5Context.initSecContext()/ KrbUtils.getTicket()/ SubjectComber.find()/ findAux()。但是,由于从未在步骤1中加载SGT,所以将找不到SGT!因此,从KDC请求并使用了一个新的SGT。
对于每个服务请求重复此步骤。
只是出于娱乐目的,并且严格作为概念验证黑客,我在登录名和initSecContext()之间添加了几行代码来解析凭据缓存,提取凭据,转换为Krb凭据并将其添加到主题的私人凭据。
完成此步骤后,在步骤2)中找到并使用了现有的SGT。 KDC不需要新的SGT。
我不会发布此hack的代码,因为它调用了我们不应调用的sun内部类,并且我不希望激发其他任何人这样做。我也无意将此黑客作为解决方案。
—>根本原因不是服务票证未保存到缓存;反而
a)没有将SGT从凭证缓存加载到客户端主体的主题
和
b)没有公共API或配置设置。
无论有没有JAAS,这都会影响GSS-API。
那这把我留在哪里呢?
i)将Java GSS-API / GSS-API与JAAS“按原样”一起使用,每个SGT请求都命中KDC —>不好。
ii)如Samson在下面的评论中所建议的,仅将Java GSS-API用于应用程序的初始登录,然后对于所有其他调用,使用 token 使用随后的调用(一种自建的kerberos-light)替代安全机制。或饼干。
iii)考虑替代GSS-API,例如Apache Kerby kerb-client。这具有超出此答案范围的含义,并且很可能证明已从众所周知的煎锅跳到了火上。
我已经向Oracle提交了Java功能请求,建议应该从缓存中检索SGT并将其存储在Subject凭证中(对于TGT来说已经如此)。
http://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8180144
问题2:没有将服务票证保存到缓存的事实是否有负面影响?
对服务票证使用凭据缓存可以减少客户端与KDC之间的交互。结果是,在未缓存服务票证的情况下,每个请求都需要与KDC进行交互,这可能导致KDC受到重创。
关于java - 未使用Java将Java GSS-API服务票证保存在凭证缓存中,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/43786908/