-1

This is nearly the same question as How to delegate the kerberos client credentials to the server?

But there is no answer. That's why I raise the question again. Hopefully someone can help.

It's possible to get a service ticket for the client (remote user) in the server side in order to use that ticket to authenticate against another backend?

Scenario: User (IE) ==> AppServer (Tomcat, under Linux) ==> Backend (webservice - REST service on Windows)

  • We have SPNEGO auth running and working in the AppServer

  • The AD user in the keytab file on in the AppServer has the rights to do the delegation (hopefully)

  • What are the preconditions that the GSSManager can create a credential that can be used for delegation? (´context.getDelegCred()´ should not fail after ´GSSManager.getInstance().createContext(this.serverCredentials)´?

There must be someone who has solved this problem?

Does "Forwardable Ticket true" mean that user from keytab file has delegation rights? Does anyone know this?

Thanks in advance

Output from HelloKDC.java (see extract from bellow)

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.

Extract from HelloKDC.java (also from 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

from 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();
    }

creates the following Exception:

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)
Uebergeek
  • 29
  • 6
  • You've got the idea right. And good job, you've diagnosed most of this problem yourself. One thing you don't have to worry about is GSSManager, that is part of an API (specifically - GSSAPI) which "just works" if your configuration files and code are right. There is a missing or wrong SPN in here somewhere. "Forwardable Ticket true" just means that the ticket itself is forward-able, it does not mean that the AppServer configuration was setup correctly. On the AD user from keytab configure delegation to the back-end server via the delegation tab of that user. Do you see that tab? – T-Heron Jan 14 '18 at 03:58
  • And also please run *setspn -Q HTTP/servername.domain.com* on any Active Directory domain-joined machine . What is the result? – T-Heron Jan 14 '18 at 03:58
  • Hello T-Heron! Thanks for your advice. I'm working in a bigger company and today I had the opportunity to work with our AD administrators. The user in the keytab file was not allowed for delegation. We entered a URL (full qualified server name without port specification) with the service type "HTTP" in the delegation tab under "Trust this user for delegation to specified services only". And then the following property was needed: ** `-Djavax.security.auth.useSubjectCredsOnly=false` ** I found it on: [link](https://dzone.com/articles/do-not-publish-configuring-tomcat-single-sign-on-w) – Uebergeek Jan 23 '18 at 13:48
  • To run the `setspn` command was not needed. I don't know it administrators maybe did that already before. – Uebergeek Jan 23 '18 at 13:56
  • Great! Pls change your solution comment into an answer and mark it as accepted - you are allowed to do that on self-solves. Please just note into what file you placed the *-Djavax.security.auth.useSubjectCredsOnly=false* property. – T-Heron Jan 23 '18 at 14:54

1 Answers1

0

As recommended I answer my own question:
First make sure to have delegation allowed in active directory for the user that is listed in the keytab file. For delegation we add a full qualified hostname and a username under which the service is running on the second sever (delegation target server - here windows). AD Delegation tab
The AD admins should know how to create a keytab file and hand it over to you.
Create a jaas.conf and a krb5.ini file like described in the question.

Use the library from http://spnego.sourceforge.net/.
Add the filter to the 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>

with all needed init parameters like described on the library web page and the filter mapping of course.

Start Tomcat with the following options:

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" 

The last option

-Djavax.security.auth.useSubjectCredsOnly=false

is very important - without that it doesn't work. That is not mentioned on the spnego.sourceforge.net website.


And then the magic really works:
The application acts as a http client with the credentials from the user who called the application from the browser.

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);
}
Uebergeek
  • 29
  • 6