Error during https call through proxy using CXF - proxy

In camel-cxf I have to call a SOAP webservice (exposed in https) through a proxy: configuring the http conduit as follows
public void configureClient(Client client) {
String proxySrv = Util.getProperty(Constants.Config.PROXY_SRV);
int proxyPort = new Integer(Util.getProperty(Constants.Config.PROXY_PORT));
log.info("Configurazione del server proxy:'"+proxySrv+"' port:'"+proxyPort+"'");
HTTPConduit conduit = (HTTPConduit) client.getConduit();
HTTPClientPolicy policy = new HTTPClientPolicy();
policy.setProxyServer(proxySrv); // set proxy host
policy.setProxyServerPort(proxyPort); // set proxy port
policy.setProxyServerType(ProxyServerType.SOCKS);
conduit.setClient(policy);
conduit.setAuthSupplier(new DefaultBasicAuthSupplier());
boolean proxyAuthEnabled = new Boolean(Util.getProperty(Constants.Config.PROXY_AUTH_EN));
String user = Util.getProperty(Constants.Config.PROXY_USER);
String pass = Util.getProperty(Constants.Config.PROXY_PASS);
log.info("Recuperati username:'+"+user+"' e password per il proxy:'"+proxySrv+"' port:'"+proxyPort+"'");
if (proxyAuthEnabled) {
ProxyAuthorizationPolicy ap = new ProxyAuthorizationPolicy();
ap.setUserName(user);
ap.setPassword(pass);
conduit.setProxyAuthorization(ap);
// conduit.getAuthorization().setUserName(user);
// conduit.getAuthorization().setPassword(pass);
log.info("Autenticazione abilitata per userName ='"+user+"' per il proxy:'"+proxySrv+"' port:'"+proxyPort+"'");
}
it works for http call (without the proxy server type set) but it doesn't work for https call. This proxy requires basic auth.
Reading various articles I saw that there is a bug in CXF that doesn't send the header authorization in the CONNECT call (and infact I'm getting 407 Authorization required -> even if with the same credentials with http calls it works).
Is there a way to fix it? I read about Olivier Billard solution
https://www.mail-archive.com/users#cxf.apache.org/msg06422.html
but I didn't undestand that solution (and I can't import at code any keystore).
Thanks

Hello I just faced this issue with the apache cxf client, the workaround suggested in the mailing list is to use the following static method of the java.net.Authenticator class :
Authenticator.setDefault(new Authenticator() {
#Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("youruser", "yourpassword".toCharArray());
}
});
This way the basic will be set automatically on all your HttpUrlConnection that uses the proxy, since java 8 you also have to enable basic authentication for HTTPS tunneling, you can do this with the following property:
-Djdk.http.auth.tunneling.disabledSchemes=""
I hope this helps

Related

How to ignore ssl on the microprofile rest client of quarkus

I'd like to ignore hostname verify and ignore client side certification validation while calling https rest api with RestClient
I cannot find a way to do it without using builder.
and seems that the hostverifier does not work at all.
public interface RHPAMRestClient {
// Starts a new process instance of a specified process.
#POST
#Path("/server/containers/{containerId}/processes/{processId}/instances")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
Object startProcess(#PathParam String containerId, #PathParam String processId, Object req);
}
RHPAMRestClient c = RestClientBuilder.newBuilder()
.baseUri(new URI(""))
.sslContext(SSLContexts.custom().loadTrustMaterial((chain, authType) -> true).build())
.hostnameVerifier((hostname, session) -> {
System.err.println("hostname verifier");
return true;
})
.build(RHPAMRestClient.class);
c.startProcess("", "", null);
It seems that there is a missconfiguration in Quarkus.
According to the documents, https://quarkus.io/guides/native-and-ssl, ssl support should be enabled when using quarkus-rest-client and the property quarkus.ssl.native should be true.
But it seems that it is false, this causes the org.jboss.resteasy.microprofile.client.RestClientBuilderImpl to override your settings
if (!SSL_ENABLED) {
resteasyClientBuilder.httpEngine((new URLConnectionClientEngineBuilder()).resteasyClientBuilder(resteasyClientBuilder).build());
resteasyClientBuilder.sslContext((SSLContext)null);
resteasyClientBuilder.trustStore((KeyStore)null);
resteasyClientBuilder.keyStore((KeyStore)null, "");
}
Forcing the property to true will magically make everything work as expected.
So, just set
quarkus.ssl.native=true in your application.properties file
(using Quarkus 1.3.1.Final)

ratpack: implementing a custom PublicAddress to force https

I was on a pac4j mail thread discussing why the redirect url ratpack-pac4j uses is using http even when the original page request is served over https. I did some digging and it looks like ratpack-pac4j is using the InferringPublicAddress. This works for local development, but because my elasticbean host proxies 443 to 80, ratpack-pac4j thinks it's over http and uses http for the redirect url. I want this call to be over https when on the server.
I'm trying to register a custom PublicAddress class that always returns http in development mode, but https in production mode:
.registry(Guice.registry(b -> {
b
.module(SessionModule.class)
.module(ThymeleafModule.class, conf -> conf.templatesMode("LEGACYHTML5"))
.add(PublicAddress.class, ForcedHttpsPublicAddress.create()); //PublicAddress.class, PublicAddress.inferred("https"));
}))
...
static private class ForcedHttpsPublicAddress implements PublicAddress
{
static ForcedHttpsPublicAddress create() {
return new ForcedHttpsPublicAddress();
}
ForcedHttpsPublicAddress() {
}
}
But when I call ctx.get(PublicAddress.class), it's still returning InferringPublicAddress. Am I registering the custom PublicAddress incorrectly?
Got help on the Ratpack forum. I needed to bind it instead of add it.
.bind(PublicAddress.class, ForcedHttpsPublicAddress.class)

Chrome headless browser with corporate proxy authetication not working

I am trying to run a Chrome headless browser sitting behind a corporate proxy. I tried below code. But unable to pass through it.
public class HeadlessChrome
{
WebDriver driver;
#Test
public void createChromeDriverHeadless() throws InterruptedException
{
System.setProperty("webdriver.chrome.driver", "D:\\LocalData\\workspace\\Drivers and Libraries\\driver\\chromedriver.exe");
ChromeOptions chromeOptions = new ChromeOptions();
Proxy proxy = new Proxy();
proxy.setHttpProxy("http://user:pwd#server:port");
proxy.setSslProxy("http://user:pwd#server:port");
// chromeOptions.setCapability("proxy", proxy);
chromeOptions.addArguments("--proxy-server=user:pwd#server:port");
chromeOptions.addArguments("--headless");
chromeOptions.addArguments("--disable-gpu");
chromeOptions.addArguments("start-maximized");
driver = new ChromeDriver(chromeOptions);
driver.get("http://seleniumhq.org");
Thread.sleep(5000);
System.out.println("Title : " + driver.getTitle());
assertTrue(driver.findElement(By.id("q")).isDisplayed());
driver.quit();
}
}
Please help me out.
If you were not using headless you could have used the approach in below link
user:pass proxies with selenium
But with headless extension are currently not allowed. So now your option is add another proxy
chrome -> (intermediate proxy w/o auth) -> corporate proxy w/ auth -> internet
One options is to use polipo
https://www.irif.fr/~jch/software/polipo/
with below config
parentAuthCredentials=username:password
parentProxy=corporateproxy:port
and then use
chromeOptions.addArguments("--proxy-server=http://polipoproxy:port");
The default would be 127.0.0.1:8123 in don't override in polipo config.
Other options you can use
Use squid proxy instead of polipo
Write your own proxy forwarder using python or node or any other language you are comfortable with
There is headless browser called Linken-sphere who cooperates with Luminati. They some good offers . you should check them up.
https://miped.ru/f/threads/linken-sphere-antidetekt-brauzer-novogo-pokolenija.67098/
It's easy to achieve with selenium 4 (currently in beta). You can do it in multiple ways:
You basically need to register a check for whether to apply the credentials for any request for authorization. Works for both - basic and proxy auth popups.
ChromeDriver driver = new ChromeDriver(new ChromeOptions().setHeadless(true));
String USER_NAME = "guest";
String PASSWORD = "guest";
//register our check here
driver.register(UsernameAndPassword.of(USER_NAME, PASSWORD));
driver.get("https://jigsaw.w3.org/HTTP/");
//Click on the link to show an authentication popup
driver.findElement(By.linkText("Basic Authentication test")).click();
String msg = driver.findElement(By.tagName("html")).getText();
assert msg.equalsIgnoreCase("Your browser made it!");
Using CDP Network domain. Doesn't work for proxy authorization popup (for example here is the similar issue in puppeteer which goes down to the chrome project)
ChromeDriver driver = new ChromeDriver(new ChromeOptions().setHeadless(true));
String USER_NAME = "guest";
String PASSWORD = "guest";
DevTools devTools = driver.getDevTools();
//create a cdp session
devTools.createSession();
//enable network first
devTools.send(Network.enable(Optional.empty(), Optional.empty(), Optional.empty()));
//Open website
driver.get("https://jigsaw.w3.org/HTTP/");
//Create and send the authorization header
Map<String, Object> headers = new HashMap<>();
String basicAuth = "Basic " + new String(Base64.getEncoder().encode(String.format("%s:%s", USER_NAME, PASSWORD).getBytes()));
headers.put("Authorization", basicAuth);
devTools.send(Network.setExtraHTTPHeaders(new Headers(headers)));
//Click on the link to show an authentication popup
driver.findElement(By.linkText("Basic Authentication test")).click();
String msg = driver.findElement(By.tagName("html")).getText();
assert msg.equalsIgnoreCase("Your browser made it!");
Using the CDP Fetch domain. Works for both - basic and proxy auth popups.
ChromeDriver driver = new ChromeDriver(new ChromeOptions().setHeadless(true));
String USER_NAME = "guest";
String PASSWORD = "guest";
DevTools devTools = driver.getDevTools();
//create a cdp session
devTools.createSession();
//enable Fetch first
devTools.send(Fetch.enable(Optional.empty(), Optional.of(true)));
devTools.addListener(Fetch.requestPaused(), requestPaused -> devTools.send(Fetch.continueRequest(requestPaused.getRequestId(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty())));
devTools.addListener(Fetch.authRequired(), authRequired -> devTools.send(Fetch.continueWithAuth(authRequired.getRequestId(), new AuthChallengeResponse(PROVIDECREDENTIALS, Optional.of(USER_NAME), Optional.of(PASSWORD)))));
//Open website
driver.get("https://jigsaw.w3.org/HTTP/");
//Click on the link to show an authentication popup
driver.findElement(By.linkText("Basic Authentication test")).click();
String msg = driver.findElement(By.tagName("html")).getText();
assert msg.equalsIgnoreCase("Your browser made it!");

websockets and authentication with identityserver4

I am using .net core 1.1 and identityserver 4 to get tokens and validate users. The web api works fine reading the bearer token from the headers and getting the user principal claims.
Now I want to use a websocket (not SignalR) for sending notifications. I can open a ws:// channel (or wss) but token isn't sent with the headers, so in the .net core application I have no information of the user (User Claims and Identity).
How can I authenticate the user through the websocket? I did a search but couldn't find any helpful information.
Thanks
There are two main problems related to the authentication in WebSocket middleware:
Authorization should be called manually
First of all, authorization is not applied to web socket request (as it is not a controller which can be marked with Authorize attribute).
That's why in WebSocket middleware you need to call authorization by your self. This is easy to achieve by calling AuthenticateAsync extension method of the HttpContext object.
So, your middleware will be look something like this:
public class WebSocketMiddleware
{
private readonly RequestDelegate next;
public WebSocketMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
if (!context.WebSockets.IsWebSocketRequest)
{
await this.next.Invoke(context);
return;
}
AuthenticateResult authenticateResult =
await context.AuthenticateAsync(OAuthValidationDefaults.AuthenticationScheme);
....
});
}
So, using authentication results you can check if the user is authenticated or not and then access authenticated user information.
Passing bearer token to web sockets request
For Web Socket connections, the default Authorization header does not work, because the WebSockets JS API doesn't allow setting custom parameters. To work around this limitation, the access token is passed quite often in the query string.
To make authentication middleware to use it, you need to update authentication validation options. This basically can be done in your startup script like this:
services
.AddAuthentication()
.AddOAuthValidation(options =>
{
options.Events = new OAuthValidationEvents
{
// Note: for Web Socket connections, the default Authorization header does not work,
// because the WebSockets JS API doesn't allow setting custom parameters.
// To work around this limitation, the access token is retrieved from the query string.
OnRetrieveToken = context =>
{
context.Token = context.Request.Query["access_token"];
return Task.FromResult(0);
}
};
});
The following code can be used as an example to add access token to web socket url during connection initializing:
const protocol = location.protocol === "https:" ? "wss:" : "ws:";
const wsUri = protocol + "//" + window.location.host + "/ws" + "?access_token=" + token;
this.socket = new WebSocket(wsUri);

what protocol to use with ADFS when security webapi for non-browser clients

Our webapi endpoints are used for both browser based clients (angular) and non-browser based clients (restsharp) and the webapi are currently secured using passive WS-Federation as the protocol and ADFS as the STS. We currently use a rather convoluted workaround for the restsharp clients since passive WS-Federation really isn't optimal for non-browser clients so we would like to find a better way to secure our webapi endpoints for these types of clients without having to replace ADFS or add extra infrastructure.
My understanding is that OAuth2 "Resource Owner Password Credentials Grant" (grant_type=password) would support this scenario nicely but unfortunately it is currently not supported by ADFS.
So, my question is this, is there a nice way to use the one OAuth2 flow that ADFS supports, namely "Authorization Code Grant Flow" (grant_type=authorization_code) to support non-browser based clients?
If this is not possible, can I secure WebApi endpoints using WS-Trust and bearer tokens without resorting to using WCF?
It turns out it was possible to use WS-Trust to get a saml 2.0 token and a WebApi to consume it with a little help from Thinktecture IdentityModel. The following does not include claims transformation so if you need to add claims to the Principal, then a little more work is needed.
The owin startup for the webapi service needs to use the following from Thinktecture.IdentityModel.Owin:
app.UseSaml2BearerAuthentication(
audience: new Uri(ConfigurationManager.AppSettings["FederatedSecurity.Realm"]),
issuerThumbprint: ConfigurationManager.AppSettings["FederatedSecurity.Thumbprint"],
issuerName: ConfigurationManager.AppSettings["FederatedSecurity.Authority"]);
For the client to request the saml 2.0 token from ADFS
private static SecurityToken RequestSecurityToken()
{
var trustChannelFactory = new WSTrustChannelFactory(new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential), new EndpointAddress(new Uri("https://yourAdfsServer/adfs/services/trust/13/usernamemixed"), new AddressHeader[0]))
{
TrustVersion = TrustVersion.WSTrust13,
Credentials = { UserName = { UserName = #"u$ern#me", Password = "p#ssw0rd" } }
};
var requestSecurityToken = new RequestSecurityToken
{
RequestType = RequestTypes.Issue,
KeyType = KeyTypes.Bearer,
TokenType = TokenTypes.Saml2TokenProfile11,
AppliesTo = new EndpointReference(_audience)
};
RequestSecurityTokenResponse response;
var securityToken = trustChannelFactory.CreateChannel().Issue(requestSecurityToken, out response);
return securityToken;
}
And for the client to call the service (using HttpClient but RestSharp will also work)
private static void CallService(SecurityToken token)
{
using (HttpClient client = new HttpClient())
{
client.SetBearerToken(Convert.ToBase64String(Encoding.UTF8.GetBytes(token.ToTokenXmlString())));
var httpMessage = client.GetAsync(new Uri(_restEndpoint)).Result;
}
}

Resources