Hi team,
I'm struggling with sample kerberos code provided in the API samples.
No matter what we are trying, we get same error from java:
GSSException: No credential found for: 1.3.6.1.5.5.2 usage: Accept
* 1.3.6.1.5.5.2 are the OID of SPNEGO protocol.
My environment:
Apigee AIO profile, with OpenJDK, Centos 7.5
Both WIN2016 and WIN2012R2 with configured KDC:
kdc: KRB.COM, kdc server - krb1.krb.com, Apigee added to DNS: api.krb.com, bi-directional DNS resolve working.
Keytab generation process:
1. Create user APIX
2. Add SPN: setspn -A HTTP/apix.krb.com apix
3. Generate keytab: ktpass -princ HTTP/apix.krb.com@KRB.COM -pass Master123 -mapuser apix -mapOp set -crypto all -pType KRB5_NT_PRINCIPAL -out apix.keytab
Made needed configuration listed here (added relevant string to MP conf-file):
conf/system.properties+java.security.auth.login.config=/opt/login.conf conf/system.properties+java.security.krb5.conf=/opt/krb5.conf
My krb5.conf file:
[logging] default = FILE:/var/log/krb5libs.log kdc = FILE:/var/log/krb5kdc.log admin_server = FILE:/var/log/kadmind.log [libdefaults] default_realm = KRB.COM dns_lookup_realm = false dns_lookup_kdc = false ticket_lifetime = 24h renew_lifetime = 7d forwardable = true [realms] KRB.COM = { kdc = krb1.krb.com admin_server = krb1.krb.com } [domain_realm] .krb.com = KRB.COM krb.com = KRB.COM
My login.conf file:
ServicePrincipalLoginContext { com.sun.security.auth.module.Krb5LoginModule required principal="HTTP/apix.krb.com@KRB.COM" doNotPrompt=true useTicketCache=true keyTab="/opt/apix.keytab" useKeyTab=true storeKey=true debug=true; };
Inside Apigee, configuration file for java-callout:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <JavaCallout async="false" continueOnError="false" enabled="true" name="credentialdelegation"> <DisplayName>CredentialDelegation</DisplayName> <FaultRules/> <Properties> <Property name="krb5Conf">/opt/krb5.conf</Property> <Property name="loginConf">/opt/login.conf</Property> <Property name="loginModule">ServicePrincipalLoginContext</Property> <Property name="serverPrincipal">HTTP/apix.krb.com@KRB.COM</Property> </Properties> <ClassName>krb5.apigee.CredentialMediation</ClassName> <ResourceURL>java://kerberos-credential-mediation.jar</ResourceURL> </JavaCallout>
We have chown apigee:apigee on all files.
After so many things I've checked, I came to conclusion that keytab is OK, KDC config also OK, but something missing in Java code(maybe). The original Javacode are lost, but @Anil Sagar @ Google was kind to decompile and provided us with class logic inside:
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package krb5.apigee; import com.apigee.flow.execution.ExecutionContext; import com.apigee.flow.execution.ExecutionResult; import com.apigee.flow.execution.spi.Execution; import com.apigee.flow.message.MessageContext; import java.security.PrivilegedAction; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.util.Map; import javax.security.auth.Subject; import javax.security.auth.login.LoginContext; import javax.xml.bind.DatatypeConverter; import org.ietf.jgss.GSSContext; import org.ietf.jgss.GSSCredential; import org.ietf.jgss.GSSException; import org.ietf.jgss.GSSManager; import org.ietf.jgss.GSSName; import org.ietf.jgss.Oid; public class CredentialMediation implements Execution { Map<String, String> properties; public CredentialMediation(Map<String, String> props) { this.properties = props; } public ExecutionResult execute(MessageContext messageContext, ExecutionContext arg1) { //System.setProperty("java.security.krb5.conf", (String)this.properties.get("krb5Conf")); //System.setProperty("java.security.auth.login.config", (String)this.properties.get("loginConf")); String loginModule = (String)this.properties.get("loginModule"); try { LoginContext loginContext = new LoginContext(loginModule); loginContext.login(); Subject serviceSubject = loginContext.getSubject(); GSSManager MANAGER = GSSManager.getInstance(); GSSContext context = MANAGER.createContext(this.getServerCredential(MANAGER, serviceSubject)); String header = (String)messageContext.getVariable("request.header.Authorization"); messageContext.setVariable("hello", "world"); messageContext.setVariable("incomingHeader", header); String header1 = header.split("Negotiate ")[1]; messageContext.setVariable("incomingToken", header1); byte[] token = DatatypeConverter.parseBase64Binary(header1); context.acceptSecContext(token, 0, token.length); System.out.println("After SecContext"); GSSCredential incomingCreds = context.getDelegCred(); System.out.println("After getDelegCred"); messageContext.setVariable("incomingSubject", context.getSrcName().toString()); System.out.println("incoming Subject"); System.out.println(context.getSrcName().toString()); System.out.println("incoming Subject printed.."); String newToken = (String)Subject.doAs(serviceSubject, new CredentialMediation.ServiceTicketGen(incomingCreds, (String)this.properties.get("serverPrincipal"))); messageContext.setVariable("newToken", newToken); messageContext.setVariable("request.header.Authorization", "Negotiate " + newToken); return ExecutionResult.SUCCESS; } catch (Exception var12) { throw new RuntimeException(var12); } } private GSSCredential getServerCredential(final GSSManager MANAGER, Subject subject) throws PrivilegedActionException { PrivilegedExceptionAction<GSSCredential> action = new PrivilegedExceptionAction<GSSCredential>() { public GSSCredential run() throws GSSException { return MANAGER.createCredential((GSSName)null, 28800, new Oid("1.2.840.113554.1.2.2"), 0); } }; return (GSSCredential)Subject.doAs(subject, action); } class ServiceTicketGen implements PrivilegedAction { private GSSCredential user; private String servicePrincipal; public ServiceTicketGen(GSSCredential user, String servicePrincipal) { this.user = user; this.servicePrincipal = servicePrincipal; } private Oid createKerberosOid() { try { return new Oid("1.2.840.113554.1.2.2"); } catch (GSSException var2) { var2.printStackTrace(); return null; } } public Object run() { byte[] outToken = (byte[])null; try { GSSManager manager = GSSManager.getInstance(); GSSName serverName = manager.createName(this.servicePrincipal, GSSName.NT_HOSTBASED_SERVICE); GSSContext context = manager.createContext(serverName, this.createKerberosOid(), this.user, 0); context.requestMutualAuth(false); context.requestConf(false); context.requestInteg(true); outToken = context.initSecContext(new byte[0], 0, 0); String out = DatatypeConverter.printBase64Binary(outToken); System.out.println(out); context.dispose(); return out; } catch (GSSException var6) { throw new RuntimeException(var6); } } } }
No matter what we did, we always get the same error:
GSSException: No credential found for: 1.3.6.1.5.5.2 usage: Accept
Google search providing absolutely no matches for this exact error.
So I recorded some WireShark to see the packets exchange: (attached .pcap)
As we can see we have a request from the client to KDC and response from KDC to the client, and then something happens inside the java (exception) and TGT ticket exchange stops. From the .pcap I don't see anything 'wrong' with the req\resp, so what's the problem?
Can some1 help to examine the problem? Maybe some1 got kerberos working on Apigee and can share?
We used this proxy as an example.
Calling all experts to help :). @Dino-at-Google @Anil Sagar @ Google @Jared Williams @Rahul
@Denis Kalitviansky : Hello Denis,
Are you able to solve this, I am getting same error.