Reputation: 11
I am using Jetty 12.x and trying to configure HttpClient with Proxy. I need to use Kerberos authentication for the proxy.
Referring the documetion https://jetty.org/docs/jetty/12/programming-guide/client/http.html#authentication, there is no help available to configure proxy with SPNEGO Authentication.The sample example https://github.com/jetty/jetty.project/blob/jetty-12.0.x/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/util/SPNEGOAuthenticationTest.java is also not helpful.
As shown in below code snippet, I am trying to access "http://example.com" via proxy where proxy details are:
String realm = "CAPVANLAB.COM";
String kdc = "capvanlab.com";
String proxyUser = "[email protected]";
String proxyPassword = "Pass@123";
String serviceName = "VanBluecoatS200.capvanlab.com"; //this is bluecoat proxy itself
String proxyHost = "192.168.72.8";
int proxyPort = 8080;
What should be the correct value of the uri passed while creating SPNEGOAuthentication() object
What should be the value of the serviceName to set in SPNEGOAuthentication object
Expected: expected SNameString of the service which was generated using HttpUrlConnection
Actual: actual Sname generated by the application here the target site is appended as the hostname in the SPN of service name which is not correct. Looking at the code of SPNEGOAuthentication.initGSSContext(), the hostname extracted from the httpRequest and is appended in the service name while generating the SPN which seems not correct.
Need help in making this work properly.
Thanks,
Below is the sample code I tried.
package com.example.caching;
import org.apache.catalina.authenticator.SpnegoAuthenticator;
import org.eclipse.jetty.client.*;
import org.eclipse.jetty.util.component.LifeCycle;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.reactive.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import javax.security.auth.Subject;
import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.Configuration;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import java.net.*;
import java.util.HashMap;
import java.util.Set;
@RestController
@Slf4j
public class CustomerController {
public String uri = "http://example.com";
String serviceType = "HTTP";
String serviceName = "VanBluecoatS200.capvanlab.com";
String scheme = "http";
String nameStr ="Capvanlabdc.Capvanlab.com";
String realm = "CAPVANLAB.COM";
String kdc = "capvanlab.com";
String clientName ="svc.bcaaa";
String proxyUser = "[email protected]";
String proxyPassword = "Pass@123";
String proxyHost = "192.168.72.8";
int proxyPort = 8080;
@GetMapping("/jet")
public String jetHttp() {
log.info(">>> checking kerberos httpClient");
setSystemProperties();
HttpClient httpClient;
try {httpClient = getJettyClient();
httpClient.start();
log.info(">>> httpClient.start() httpClient called");
ContentResponse res = httpClient.GET(uri);
log.info("res.getStatus() {}", res.getStatus());
String result = res.getContentAsString();
new Thread(() -> LifeCycle.stop(httpClient)).start();
return result;
} catch(Exception e){
log.warn(">>> Exception " + e);
return "Exception occurred";
}
}
public void setSystemProperties() {
log.info("Setting Kerberos Realm and KDC");
System.setProperty("java.security.krb5.realm", realm);
System.setProperty("java.security.krb5.kdc", kdc);
System.setProperty("sun.security.krb5.debug", "true");
Configuration.setConfiguration(setJaasConfigParams());
log.info("Kerberos Realm and KDC are succesfull set to the perf qualification ");
}
private Configuration setJaasConfigParams() {
log.info("calling setJaasConfigParams");
return new Configuration() {
public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
HashMap<String, Object> options = new HashMap<>();
options.put("useTicketCache", "false");
AppConfigurationEntry appConfigurationEntry = new AppConfigurationEntry(
"com.sun.security.auth.module.Krb5LoginModule",
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options);
return new AppConfigurationEntry[]{appConfigurationEntry};
}
};
}
private HttpClient getJettyClient() throws URISyntaxException {
HttpClient httpClient = new HttpClient();
configProxyJettyClient(httpClient);
return httpClient;
}
private void configProxyJettyClient(HttpClient httpClient) throws URISyntaxException {
log.info("configProxyJettyClient start");
URI spnegoURI = URI.create("http://" + proxyHost + ":" +proxyPort);
SPNEGOAuthentication spnegoAuth = new SPNEGOAuthentication(spnegoURI);
spnegoAuth.setUserName(clientName + "@" + realm); // Or use Kerberos principal
spnegoAuth.setUserPassword(proxyPassword); // Not required if using keytab
spnegoAuth.setServiceName(serviceName);
AuthenticationStore auth = httpClient.getAuthenticationStore();
auth.addAuthentication(spnegoAuth);
HttpProxy proxy = new HttpProxy(proxyHost, proxyPort);
ProxyConfiguration proxyConfig = httpClient.getProxyConfiguration();
proxyConfig.addProxy(proxy);
log.info("configProxyJettyClient end");
}
}
Upvotes: 1
Views: 107
Reputation: 16532
What should be the correct value of the uri passed while creating SPNEGOAuthentication() object
The only thing the constructor does with the URI is pass it to super(), so it's the same with all authentication handlers. It seems to me that is only used to match whether the Authentication object should be used at all, and should be http://ProxyHost:ProxyPort
as you're currently doing.
What should be the value of the serviceName to set in SPNEGOAuthentication object
The GSSAPI service name can be in two forms, service@hostname
(generic GSSAPI 'host-based service' style) and service/hostname[@REALM]
(Kerberos style). I believe Java prefers GSSAPI format, while native Windows SSPI would insist on Kerberos format.
For SPNEGO via HTTP(S), the service is normally HTTP
(upper-case). For nearly all protocols (including HTTP), the hostname is generally the server's FQDN (lower-case preferred).
What's important is that the service name matches the SPN defined in the KDC, so that the client could request a ticket for the right service. (Active Directory doesn't care about upper/lower case, but other kinds of Kerberos KDCs do, so try to keep it consistent.)
So if you're using AD and the service account has the SPN of HTTP/blah.example.com
assigned, then the service name in GSSAPI syntax would be [email protected]
, and therefore:
String serviceName = "[email protected]";
(If you specify a string that has neither an @
nor a /
, then it's taken to be just the service name, and it'll usually use the local FQDN as the "hostname" which is practically always wrong.)
Upvotes: 0