Retrieve the kerberos token from KDC server

Hello,

can anyone help me on this.

Below is my requirement

1. User posts JWT token.

2. need to validate JWT token and extract UPN

3. authenticate KDC server with UPN and Keytab file and retrieve kereberos token

and I am stuck up at step 3

Getting error from apigee while calling java callout

Error running java callout java.lang.RuntimeException: javax.security.auth.login.LoginException: No LoginModules configured for customclient


Below is my java callout properties

-----------

<JavaCallout async="false" continueOnError="false" enabled="true" name="JavaCallout-GenerateKerb"> <DisplayName>JavaCallout-GenerateKerb</DisplayName> <Properties>

<Property name="krb5Conf">krb5.conf</Property>

<Property name="loginConf">login1.conf</Property>

<Property name="loginModule">customclient</Property>

<Property name="serverPrincipal">HTTP/Svc-apigee-kerb</Property>

</Properties> <ClassName>main.java.GenerateKerberos</ClassName> <ResourceURL>java://Kerb1.0.jar</ResourceURL> </JavaCallout>

------------------


This is failing at the below step in JAva code

String loginModule = (String)properties.get("loginModule");

1 26 1,428
26 REPLIES 26

You are following along with this?

https://github.com/apigee/api-platform-samples/tree/master/sample-proxies/kerberos-credential-mediat...

What do you mean when you say the following line is "failing"?

String loginModule = (String)properties.get("loginModule");

I think, with the configuration you are showing, the Java callout cannot :fail: at that line. It will return a string ("customclient"), or null. It won't "fail".

Also you said the failure is "No LoginModules configured for customclient"

That is a different symptom. That error or exception won't be generated by the line of code you cited.

Have you configured the login.conf and krb5.conf? Show them.

Where do you have these files?

Give us more details.

deniska
Participant V

I also want to raise question on 'where to put the .conf files and keytab'.

/opt/apigee

/opt/

/opt/apigee/messageprocessor location

not working. Please assist?

@Maruti Chand

Hi, can you please help on the subject?

tried placing the files under /opt/apigee and /opt/apigee/messageprocessor.

both didn't worked.

placed both conf and keytab files at different locations and tried

/opt/apigee

/opt/apigee/messageprocessor

/opt/apigee/messageprocessor/conf

@JSurapaneni , @Denis Kalitviansky , @Jared Williams ,

You need to change few settings to make it work.

You need to add below properties to message processor, Find out how to do same, below,

java.security.krb5.conf=/opt/krb5.conf

java.security.auth.login.config=/opt/login.conf

Step 1 : Edit vi /opt/apigee/customer/application/message-processor.properties

Step 2 : Add below line to above file at the end

conf/system.properties+java.security.auth.login.config=/opt/login.conf conf/system.properties+java.security.krb5.conf=/opt/krb5.conf

Step 3 : Restart Message Processor

Step 4: Verify cat /opt/apigee/edge-message-processor/conf/system.properties

Prints above two properties at end.

Place krb5.conf & login.conf in /opt directory & make sure permissions are taken care.

Above error should go away. Hope it helps.

Thanks @Anil Sagar @ Google! I just tested and can confirm that this does indeed resolve the "No LoginModules configured for..." exception.

Awesome ! Glad it worked !!

For me that error indeed go away, but I have new one - and it seems all configured normally

{"fault":{"faultstring":"Failed to execute JavaCallout. java.lang.SecurityException: java.io.IOException: Configuration Error:\n\texpected [;], read [end of file]","detail":{"errorcode":"steps.javacallout.ExecutionError"}}}

Can you maybe share your conf files? OR the github ones is that working?

@Denis Kalitviansky There needs to be a semicolon at the very end of your ServicePrincipalLoginContext definition in login.conf (this semicolon is also missing from the github definition):

ServicePrincipalLoginContext
{
      com.sun.security.auth.module.Krb5LoginModule required 
      principal="http/service-principal-account@APIGEE.LOCAL" 
      doNotPrompt=true
      useTicketCache=true   
      keyTab="spn.keytab"
      useKeyTab=true
      storeKey=true
      debug=true;      
};

Thank you very much I will check on that !

btw, Jared, di dyou manage to understand how to work in two different modes? From github I understand I can both 'protect' my proxy with Kerberos authentication and also to work to my backend that are protected with Kerberos. In situation where I want to work only as backend client to Kerberos - is it configurable?

@JSurapaneni , @Jared Williams , @Denis Kalitviansky ,

Here is the sample proxy that successfully worked for me !

helloworldkerbproxy-rev11-2017-11-03.zip

Hi Anil, in the documentation written that there can be two modes, e.g. I can validate incoming Kerberos ticket against KDC server, and new ticket will be generated to backend from my keystore. My question where I can choose if my proxy requires Kerberos or when my proxy is not protected by Kerberos but my real backend is and I need to authenticate to it as client.

Thanks

@Anil Sagar @ Google, Hi Anil, any assistance please?

deniska
Participant V

Dear community, please help with separation of the modes if possible. We want in some cases to just protect our proxy with Kerberos (without authenticating to backed with kerberos also), and in some cases to authenticate to backend Kerberos protected API while leaving front-end proxy unprotected.

@Anil Sagar @ Google any source code available for this functionality so we can make it?

@Denis Kalitviansky , You can use sample proxy attached above.

Hi @Anil Sagar @ Google, your proxy is doing the two steps both: protect facade with Kerberos and also talk to backend with kerberos... i want to split it... possible? - for example I want to authenticate to BACKEND API's only with kerberos, but my Apigee proxy facade is available without Kerb

@Denis Kalitviansky ,

Here you go original java file, split it like you need, Hope it helps.

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.io.PrintStream;
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)
  {
    properties = props;
  }
  
  public ExecutionResult execute(MessageContext messageContext, ExecutionContext arg1)
  {
    System.setProperty("java.security.krb5.conf", (String)properties.get("krb5Conf"));
    System.setProperty("java.security.auth.login.config", (String)properties.get("loginConf"));
    String loginModule = (String)properties.get("loginModule");
    try
    {
      LoginContext loginContext = new LoginContext(loginModule);
      loginContext.login();
      Subject serviceSubject = loginContext.getSubject();
      GSSManager MANAGER = GSSManager.getInstance();
      GSSContext context = MANAGER.createContext(getServerCredential(MANAGER, serviceSubject));
      
      String header = (String)messageContext.getVariable("request.header.Authorization");
      header = header.split("Negotiate ")[1];
      byte[] token = DatatypeConverter.parseBase64Binary(header);
      context.acceptSecContext(token, 0, token.length);
      GSSCredential incomingCreds = context.getDelegCred();
      messageContext.setVariable("incomingSubject", context.getSrcName().toString());
      
      String newToken = (String)Subject.doAs(serviceSubject, new ServiceTicketGen(incomingCreds, (String)properties.get("serverPrincipal")));
      messageContext.setVariable("newToken", newToken);
      messageContext.setVariable("request.header.Authorization", "Negotiate " + newToken);
      return ExecutionResult.SUCCESS;
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }
  


  private GSSCredential getServerCredential(final GSSManager MANAGER, Subject subject)
    throws PrivilegedActionException
  {
    PrivilegedExceptionAction<GSSCredential> action = 
      new PrivilegedExceptionAction() {
        public GSSCredential run() throws GSSException {
          return MANAGER.createCredential(
            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 e) {
          e.printStackTrace();
        }
        return null;
      }
      
      public Object run() {
        byte[] outToken = (byte[])null;
        try {
          GSSManager manager = GSSManager.getInstance();
          
          GSSName serverName = manager.createName(servicePrincipal, GSSName.NT_HOSTBASED_SERVICE);
          GSSContext context = manager.createContext(serverName, 
            createKerberosOid(), 
            user, 
            0);
          context.requestMutualAuth(true);
          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 e) {
          throw new RuntimeException(e);
        }
      }
    }
  }



Thank you very much!

Not applicable
@Anil Sagar @ Google

I have followed all guidelines mentioned in your previous comments, my kerberos is only generating ticket when I'm sending UPN same as SPN and it;s not working when i send user email for authenticating. For eg. if SPN is http/MYLDAP.COM and UPN is also http/MYLDAP.COM then ticket is generated , if UPN is something like http/RAHUL@MYLDAP.COM it won't work.

Could you please help

Hi @Anil Sagar @ Google @Dino-at-Google. Have either of you run into Rahul's situation before?

@Jared Williams , Ideally, It should work. I haven't come across similar situation.

Rahul, if you are receiving an exception, could you please paste the exception and full stack trace. Alternatively, can you add message logging to the java callout (or check any logs you may be capturing downstream) and paste those here?

For more information about java callout logging, please check this community post: https://community.apigee.com/questions/20840/acces-to-java-log-messages.html

This cookbook may also be helpful in understanding how you can gain more visibility into, and handle java callout errors: https://docs.apigee.com/api-platform/samples/cookbook/how-handle-java-callout-errors