如何在 linux 服务器 运行 Tomcat 上委派 kerberos 客户端凭据?
How to delegate the kerberos client credentials on a linux server running Tomcat?
这几乎与 How to delegate the kerberos client credentials to the server?
相同的问题
但是没有答案。这就是为什么我再次提出这个问题。希望有人能帮忙。
是否可以在服务器端为客户端(远程用户)获取服务票据,以便使用该票据对另一个后端进行身份验证?
场景:用户 (IE) ==> AppServer(Tomcat,在 Linux 下)==> 后端(网络服务 - Windows 上的 REST 服务)
我们有 SPNEGO 授权 运行 并在 AppServer 中工作
AppServer中keytab文件中的AD用户有权
做代表团(希望如此)
GSSManager可以创建一个的先决条件是什么
可用于委派的凭据? (´context.getDelegCred()´
之后不应该失败
´GSSManager.getInstance().createContext(this.serverCredentials)´?
一定有人解决过这个问题吧?
"Forwardable Ticket true"是否意味着密钥表文件中的用户具有委托权限?有人知道吗?
提前致谢
来自 HelloKDC.java 的输出(请参阅下面的摘录)
Client Principal = HTTP/servername.domain.com@CORP1.AD1.COMPANY.NET
Server Principal = krbtgt/CORP1.AD1.COMPANY.NET@CORP1.AD1.COMPANY.NET
Session Key = EncryptionKey: keyType=23 keyBytes (hex dump)=
0000: xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx ................
Forwardable Ticket true
Forwarded Ticket false
Proxiable Ticket false
Proxy Ticket false
Postdated Ticket false
Renewable Ticket false
Initial Ticket false
Auth Time = Wed Dec 20 16:52:03 CET 2017
Start Time = Wed Dec 20 16:52:03 CET 2017
End Time = Thu Dec 21 02:52:03 CET 2017
Renew Till = null
Client Addresses Null
Private Credential: /opt/app/tomcat/ssoad1/servername.domain.com.keytab for HTTP/servername.domain.com@CORP1.AD1.COMPANY.NET
Connection test successful.
摘自HelloKDC.java(也来自net.sourceforge.spnego):
// Name of our krb5 config file
final String krbfile = "/opt/app/tomcat/ssoad1/krb5.ini";
// Name of our login config file
final String loginfile = "/opt/app/tomcat/ssoad1/jaas.conf";
// Name of our login module
//final String module = "spnego-client";
final String module = "com.sun.security.jgss.krb5.initiate";
// set some system properties
System.setProperty("java.security.krb5.conf", krbfile);
System.setProperty("java.security.auth.login.config", loginfile);
System.setProperty("sun.security.krb5.debug", "true");
final LoginContext loginContext = new LoginContext(module);
// attempt to login
loginContext.login();
// output some info
System.out.println("Subject=" + loginContext.getSubject());
// logout
loginContext.logout();
System.out.println("Connection test successful.");
jaas.conf:
com.sun.comcurity.jgss.krb5.initiate {
com.sun.comcurity.auth.module.Krb5LoginModule required
doNotPrompt=true
principal="HTTP/servername.domain.com@CORP1.AD1.COMPANY.NET"
useKeyTab=true
keyTab="/opt/app/tomcat/ssoad1/servername.domain.com.keytab"
storeKey=true;
};
com.sun.security.jgss.krb5.accept {
com.sun.security.auth.module.Krb5LoginModule required
doNotPrompt=true
principal="HTTP/servername.domain.com@CORP1.AD1.COMPANY.NET"
useKeyTab=true
keyTab="/opt/app/tomcat/ssoad1/servername.domain.com.keytab"
storeKey=true
useTicketCache=true
isInitiator=true
refreshKrb5Config=true
moduleBanner=true
storePass=true;
};
spnego-client {
com.sun.security.auth.module.Krb5LoginModule required;
};
spnego-server {
com.sun.security.auth.module.Krb5LoginModule required
principal="HTTP/servername.domain.com@CORP1.AD1.COMPANY.NET"
useKeyTab=true
keyTab="/opt/app/tomcat/ssoad1/servername.domain.com.keytab"
storeKey=true
useTicketCache=true
isInitiator=false
refreshKrb5Config=true
moduleBanner=true
;
};
krb5.ini
[libdefaults]
default_realm = CORP1.AD1.COMPANY.NET
default_keytab_name = FILE:/opt/app/tomcat/ssoad1/servername.domain.com.keytab
default_tkt_enctypes = rc4-hmac
default_tgs_enctypes = rc4-hmac
forwardable = true
renewable = true
noaddresses = true
clockskew = 300
udp_preference_limit = 1
[realms]
CORP1.AD1.COMPANY.NET = {
kdc = ndcr001k.corp1.ad1.company.net:88
default_domain = domain.com
}
[domain_realm]
.domain.com = CORP1.AD1.COMPANY.NET
来自 net.sourceforge.spnego.SpnegoAuthenticator.java
SpnegoAuthenticator.LOCK.lock();
try {
LOGGER.fine("create context");
LOGGER.fine("serverCredentials="+this.serverCredentials.toString());
context = SpnegoAuthenticator.MANAGER.createContext(this.serverCredentials);
context.requestCredDeleg(true);
LOGGER.fine("clientModuleName="+clientModuleName.toString());
LOGGER.fine("context.getCredDelegState()="+context.getCredDelegState());
token = context.acceptSecContext(gss, 0, gss.length); // When I understand right : gss contains the token from the authorized client (IE Windows user)
LOGGER.fine("token="+token);
LOGGER.fine("context.getDelegCred()="+context.getDelegCred());
} finally {
SpnegoAuthenticator.LOCK.unlock();
}
创建以下异常:
javax.servlet.ServletException: GSSException: No valid credentials provided
net.sourceforge.spnego.SpnegoHttpFilter.doFilter(SpnegoHttpFilter.java:287)
Root Cause
GSSException: No valid credentials provided
sun.security.jgss.krb5.Krb5Context.getDelegCred(Krb5Context.java:511)
sun.security.jgss.GSSContextImpl.getDelegCred(GSSContextImpl.java:614)
sun.security.jgss.spnego.SpNegoContext.getDelegCred(SpNegoContext.java:1064)
sun.security.jgss.GSSContextImpl.getDelegCred(GSSContextImpl.java:614)
net.sourceforge.spnego.SpnegoAuthenticator.doSpnegoAuth(SpnegoAuthenticator.java:503)
按照建议我回答我自己的问题:
首先确保在 keytab 文件中列出的用户的活动目录中允许委派。对于委托,我们在第二台服务器(委托目标服务器 - 此处为 windows)上添加一个完全限定的主机名和一个用户名,在该用户名下服务是 运行。
AD Delegation tab
AD 管理员应该知道如何创建密钥表文件并将其交给您。
创建一个 jaas.conf 和一个 krb5.ini 文件,如问题中所述。
使用来自 http://spnego.sourceforge.net/ 的库。
将过滤器添加到 web.xml:
<filter>
<filter-name>SpnegoHttpFilter</filter-name>
<filter-class>net.sourceforge.spnego.SpnegoHttpFilter</filter-class>
....
....
<init-param>
<param-name>spnego.allow.delegation</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>SpnegoHttpFilter</filter-name>
<url-pattern>/sc2</url-pattern>
</filter-mapping>
具有所有需要的初始化参数,如库网页上所述,当然还有过滤器映射。
使用以下选项启动 Tomcat:
CATALINA_OPTS="-Dsun.security.krb5.debug=[true|false]
-Djava.security.auth.login.config=/opt/app/tomcat/ssoad1/jaas.conf
-Djava.security.krb5.conf=/opt/app/tomcat/ssoad1/krb5.ini
-Djavax.security.auth.useSubjectCredsOnly=false"
最后一个选项
-Djavax.security.auth.useSubjectCredsOnly=false
非常重要 - 没有它就无法工作。 spnego.sourceforge.net 网站上没有提及。
然后魔法真的起作用了:
该应用程序充当 http 客户端,其中包含从浏览器调用该应用程序的用户的凭据。
private void doServiceCall(HttpServletRequest request, StringBuilder sb) throws GSSException, MalformedURLException, PrivilegedActionException, IOException {
if (request instanceof DelegateServletRequest) {
DelegateServletRequest dsr = (DelegateServletRequest) request;
GSSCredential creds = dsr.getDelegatedCredential();
if (null == creds) {
sb.append("No delegated creds.");
} else {
sb.append(creds.getName().toString());
SpnegoHttpURLConnection spnego =
new SpnegoHttpURLConnection(creds);
HttpURLConnection con = spnego.connect(new URL("https://server.domain.com/ServiceFactory/servicenamexyz/Get?KeyConditionValue=ACTION_OUTPUT"));
sb.append("<br />HTTP Status Code: " + spnego.getResponseCode());
sb.append("<br />HTTP Status Message: " + spnego.getResponseMessage());
String contentType = con.getContentType();
sb.append("<br />HTTP Content Type: " + contentType);
StringBuilder result = new StringBuilder();
String line;
BufferedReader reader = new BufferedReader(new InputStreamReader(con.getInputStream()));
while ((line = reader.readLine()) != null) {
result.append(line);
}
reader.close();
sb.append("<br />HTTP Content: " + result.toString());
spnego.disconnect();
}
} else {
sb.append("Request not a delegate.");
}
br(sb);
}
这几乎与 How to delegate the kerberos client credentials to the server?
相同的问题但是没有答案。这就是为什么我再次提出这个问题。希望有人能帮忙。
是否可以在服务器端为客户端(远程用户)获取服务票据,以便使用该票据对另一个后端进行身份验证?
场景:用户 (IE) ==> AppServer(Tomcat,在 Linux 下)==> 后端(网络服务 - Windows 上的 REST 服务)
我们有 SPNEGO 授权 运行 并在 AppServer 中工作
AppServer中keytab文件中的AD用户有权 做代表团(希望如此)
GSSManager可以创建一个的先决条件是什么 可用于委派的凭据? (´context.getDelegCred()´ 之后不应该失败 ´GSSManager.getInstance().createContext(this.serverCredentials)´?
一定有人解决过这个问题吧?
"Forwardable Ticket true"是否意味着密钥表文件中的用户具有委托权限?有人知道吗?
提前致谢
来自 HelloKDC.java 的输出(请参阅下面的摘录)
Client Principal = HTTP/servername.domain.com@CORP1.AD1.COMPANY.NET
Server Principal = krbtgt/CORP1.AD1.COMPANY.NET@CORP1.AD1.COMPANY.NET
Session Key = EncryptionKey: keyType=23 keyBytes (hex dump)=
0000: xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx ................
Forwardable Ticket true
Forwarded Ticket false
Proxiable Ticket false
Proxy Ticket false
Postdated Ticket false
Renewable Ticket false
Initial Ticket false
Auth Time = Wed Dec 20 16:52:03 CET 2017
Start Time = Wed Dec 20 16:52:03 CET 2017
End Time = Thu Dec 21 02:52:03 CET 2017
Renew Till = null
Client Addresses Null
Private Credential: /opt/app/tomcat/ssoad1/servername.domain.com.keytab for HTTP/servername.domain.com@CORP1.AD1.COMPANY.NET
Connection test successful.
摘自HelloKDC.java(也来自net.sourceforge.spnego):
// Name of our krb5 config file
final String krbfile = "/opt/app/tomcat/ssoad1/krb5.ini";
// Name of our login config file
final String loginfile = "/opt/app/tomcat/ssoad1/jaas.conf";
// Name of our login module
//final String module = "spnego-client";
final String module = "com.sun.security.jgss.krb5.initiate";
// set some system properties
System.setProperty("java.security.krb5.conf", krbfile);
System.setProperty("java.security.auth.login.config", loginfile);
System.setProperty("sun.security.krb5.debug", "true");
final LoginContext loginContext = new LoginContext(module);
// attempt to login
loginContext.login();
// output some info
System.out.println("Subject=" + loginContext.getSubject());
// logout
loginContext.logout();
System.out.println("Connection test successful.");
jaas.conf:
com.sun.comcurity.jgss.krb5.initiate {
com.sun.comcurity.auth.module.Krb5LoginModule required
doNotPrompt=true
principal="HTTP/servername.domain.com@CORP1.AD1.COMPANY.NET"
useKeyTab=true
keyTab="/opt/app/tomcat/ssoad1/servername.domain.com.keytab"
storeKey=true;
};
com.sun.security.jgss.krb5.accept {
com.sun.security.auth.module.Krb5LoginModule required
doNotPrompt=true
principal="HTTP/servername.domain.com@CORP1.AD1.COMPANY.NET"
useKeyTab=true
keyTab="/opt/app/tomcat/ssoad1/servername.domain.com.keytab"
storeKey=true
useTicketCache=true
isInitiator=true
refreshKrb5Config=true
moduleBanner=true
storePass=true;
};
spnego-client {
com.sun.security.auth.module.Krb5LoginModule required;
};
spnego-server {
com.sun.security.auth.module.Krb5LoginModule required
principal="HTTP/servername.domain.com@CORP1.AD1.COMPANY.NET"
useKeyTab=true
keyTab="/opt/app/tomcat/ssoad1/servername.domain.com.keytab"
storeKey=true
useTicketCache=true
isInitiator=false
refreshKrb5Config=true
moduleBanner=true
;
};
krb5.ini
[libdefaults]
default_realm = CORP1.AD1.COMPANY.NET
default_keytab_name = FILE:/opt/app/tomcat/ssoad1/servername.domain.com.keytab
default_tkt_enctypes = rc4-hmac
default_tgs_enctypes = rc4-hmac
forwardable = true
renewable = true
noaddresses = true
clockskew = 300
udp_preference_limit = 1
[realms]
CORP1.AD1.COMPANY.NET = {
kdc = ndcr001k.corp1.ad1.company.net:88
default_domain = domain.com
}
[domain_realm]
.domain.com = CORP1.AD1.COMPANY.NET
来自 net.sourceforge.spnego.SpnegoAuthenticator.java
SpnegoAuthenticator.LOCK.lock();
try {
LOGGER.fine("create context");
LOGGER.fine("serverCredentials="+this.serverCredentials.toString());
context = SpnegoAuthenticator.MANAGER.createContext(this.serverCredentials);
context.requestCredDeleg(true);
LOGGER.fine("clientModuleName="+clientModuleName.toString());
LOGGER.fine("context.getCredDelegState()="+context.getCredDelegState());
token = context.acceptSecContext(gss, 0, gss.length); // When I understand right : gss contains the token from the authorized client (IE Windows user)
LOGGER.fine("token="+token);
LOGGER.fine("context.getDelegCred()="+context.getDelegCred());
} finally {
SpnegoAuthenticator.LOCK.unlock();
}
创建以下异常:
javax.servlet.ServletException: GSSException: No valid credentials provided
net.sourceforge.spnego.SpnegoHttpFilter.doFilter(SpnegoHttpFilter.java:287)
Root Cause
GSSException: No valid credentials provided
sun.security.jgss.krb5.Krb5Context.getDelegCred(Krb5Context.java:511)
sun.security.jgss.GSSContextImpl.getDelegCred(GSSContextImpl.java:614)
sun.security.jgss.spnego.SpNegoContext.getDelegCred(SpNegoContext.java:1064)
sun.security.jgss.GSSContextImpl.getDelegCred(GSSContextImpl.java:614)
net.sourceforge.spnego.SpnegoAuthenticator.doSpnegoAuth(SpnegoAuthenticator.java:503)
按照建议我回答我自己的问题:
首先确保在 keytab 文件中列出的用户的活动目录中允许委派。对于委托,我们在第二台服务器(委托目标服务器 - 此处为 windows)上添加一个完全限定的主机名和一个用户名,在该用户名下服务是 运行。
AD Delegation tab
AD 管理员应该知道如何创建密钥表文件并将其交给您。
创建一个 jaas.conf 和一个 krb5.ini 文件,如问题中所述。
使用来自 http://spnego.sourceforge.net/ 的库。
将过滤器添加到 web.xml:
<filter>
<filter-name>SpnegoHttpFilter</filter-name>
<filter-class>net.sourceforge.spnego.SpnegoHttpFilter</filter-class>
....
....
<init-param>
<param-name>spnego.allow.delegation</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>SpnegoHttpFilter</filter-name>
<url-pattern>/sc2</url-pattern>
</filter-mapping>
具有所有需要的初始化参数,如库网页上所述,当然还有过滤器映射。
使用以下选项启动 Tomcat:
CATALINA_OPTS="-Dsun.security.krb5.debug=[true|false]
-Djava.security.auth.login.config=/opt/app/tomcat/ssoad1/jaas.conf
-Djava.security.krb5.conf=/opt/app/tomcat/ssoad1/krb5.ini
-Djavax.security.auth.useSubjectCredsOnly=false"
最后一个选项
-Djavax.security.auth.useSubjectCredsOnly=false
非常重要 - 没有它就无法工作。 spnego.sourceforge.net 网站上没有提及。
然后魔法真的起作用了:
该应用程序充当 http 客户端,其中包含从浏览器调用该应用程序的用户的凭据。
private void doServiceCall(HttpServletRequest request, StringBuilder sb) throws GSSException, MalformedURLException, PrivilegedActionException, IOException {
if (request instanceof DelegateServletRequest) {
DelegateServletRequest dsr = (DelegateServletRequest) request;
GSSCredential creds = dsr.getDelegatedCredential();
if (null == creds) {
sb.append("No delegated creds.");
} else {
sb.append(creds.getName().toString());
SpnegoHttpURLConnection spnego =
new SpnegoHttpURLConnection(creds);
HttpURLConnection con = spnego.connect(new URL("https://server.domain.com/ServiceFactory/servicenamexyz/Get?KeyConditionValue=ACTION_OUTPUT"));
sb.append("<br />HTTP Status Code: " + spnego.getResponseCode());
sb.append("<br />HTTP Status Message: " + spnego.getResponseMessage());
String contentType = con.getContentType();
sb.append("<br />HTTP Content Type: " + contentType);
StringBuilder result = new StringBuilder();
String line;
BufferedReader reader = new BufferedReader(new InputStreamReader(con.getInputStream()));
while ((line = reader.readLine()) != null) {
result.append(line);
}
reader.close();
sb.append("<br />HTTP Content: " + result.toString());
spnego.disconnect();
}
} else {
sb.append("Request not a delegate.");
}
br(sb);
}