Closed mediga closed 8 years ago
I need a bit more info to understand your test scenario. I can confirm regular SSL connections on a normal network (not your corporate network) work fine: we have test cases for those and they pass.
Does your corporate network itself require a proxy to work? In other words: are you trying to chain proxies? If so can you tell me how you're configuring that?
Hello Patrick,
For me to make connection to internet from within my corporate network, i need to connect via corporate upstream proxy. Here is the network topology.
(my computer) <<---->> (BrowserMob proxy) <<---->> (Corporate Upstream Proxy) <<--->> (internet sites)
I am configuring the upstream proxy in the code below and passing the configuraton to browsermob.
This configuration works great with http sites, but does not work with https.
I am pasting my test class here.
package com.example.tests.bmp;
import java.io.File; import java.net.URI; import java.net.URL; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map;
import net.lightbody.bmp.core.har.Har; import net.lightbody.bmp.proxy.ProxyServer; import net.lightbody.bmp.proxy.http.BrowserMobHttpRequest; import net.lightbody.bmp.proxy.http.RequestInterceptor;
import org.openqa.selenium.Proxy; import org.openqa.selenium.WebDriver; import org.openqa.selenium.firefox.FirefoxDriver; import org.openqa.selenium.firefox.FirefoxProfile; import org.openqa.selenium.remote.CapabilityType; import org.openqa.selenium.remote.DesiredCapabilities;
public class Test2 {
public static final Map<String, String> hostHeaderSpoofMap;
static {
hostHeaderSpoofMap = new LinkedHashMap<String, String>();
hostHeaderSpoofMap.put("www.google.com", "www.google.com");
}
public static void main(String[] args) {
//set this to true if need to tunnel through outgoing corporate traffic
boolean enableOutgoingCorpProxy=true;
final String authUser = "<username>";
final String authPassword = "<Password>"; //set network password
int MOB_PROXY_PORT=8105;
String MOB_PROXY_HOST="localhost";
try{
String strFilePath = "C:\\testmystuff\\testhar\\";
ProxyServer mobProxyServer = new ProxyServer(8105);
Map<String,String> mobProxyServerOptions = new HashMap<String, String>();
mobProxyServerOptions.put("httpProxy", "<proxyHost>:<proxyPort>");
try{
//stop if already running
mobProxyServer.stop();
}catch(Exception e){e.printStackTrace();}
mobProxyServer.start();
// set corporate outgoing proxy
if(enableOutgoingCorpProxy){
mobProxyServer.setOptions(mobProxyServerOptions);
mobProxyServer.autoBasicAuthorization("AD",authUser, authPassword);
}
mobProxyServer.setCaptureHeaders(true);
mobProxyServer.setCaptureContent(true);
Proxy mobProxySeleniumClient = new Proxy();
String mobProxyString = MOB_PROXY_HOST + ":" + MOB_PROXY_PORT;
// mobProxy.setHttpProxy(proxyString);//.setSslProxy(proxyString);
mobProxySeleniumClient.setHttpProxy("");
mobProxySeleniumClient.setSslProxy(mobProxyString);
WebDriver driver = createWebDriver(mobProxySeleniumClient);
String pageUrl="https://www.google.com";
URL url = new URL(pageUrl);
mobProxyServer.addRequestInterceptor(new RequestInterceptor() {
@Override
public void process(BrowserMobHttpRequest request, Har har) {
URI requestURI=request.getMethod().getURI();
String requestHost=requestURI.getHost();
if(hostHeaderSpoofMap.containsKey(requestHost)){
System.out.println("request Intercepted");
System.out.println("request uri:" + requestURI.toString());
System.out.println("Host header -Before :" + request.getMethod().getHeaders("Host")[0]);
//spoof the host header for
request.getMethod().setHeader("Host",hostHeaderSpoofMap.get(requestHost) );
System.out.println("Host header -After>> :" + request.getMethod().getHeaders("Host")[0]);
System.out.println(">>>>>>");
System.out.println("");
}
}
});
mobProxyServer.newHar(pageUrl);
driver.get(pageUrl);
mobProxyServer.clearBlacklist();
Har har = mobProxyServer.getHar();
System.out.println("### HAR file generated");
System.out.println(har.getLog().getEntries());
har.writeTo(new File(strFilePath+url.getHost()+url.getPath().replace("/", ".")+".har"));
mobProxyServer.waitForNetworkTrafficToStop(40000, 20000);
mobProxyServer.stop();
}catch(Exception e){
e.printStackTrace();
}
}
private static WebDriver createWebDriver(Proxy proxy){
// configure it as a desired capability
DesiredCapabilities capabilities = new DesiredCapabilities();
FirefoxProfile profile = new FirefoxProfile();
profile.setAcceptUntrustedCertificates(true);
profile.setAssumeUntrustedCertificateIssuer(true);
profile.setPreference("network.proxy.http", "localhost");
profile.setPreference("network.proxy.http_port", 8105);
proxy.setProxyType(Proxy.ProxyType.MANUAL);
capabilities.setCapability(FirefoxDriver.PROFILE,profile);
capabilities.setCapability(CapabilityType.PROXY, proxy);
capabilities.setCapability(CapabilityType.ACCEPT_SSL_CERTS, true);
// start the browser up
WebDriver driver = new FirefoxDriver(capabilities);
return driver;
}
}
Hello Patrick, do you have any suggestions on what might be going wrong and if there is workaround?
Sorry been busy. I'll try to help debug soon.
Hello Patrick, can you provide me suggestion on what to look for? if there is a debug flag that i can turn on to see what is happening in the browser mob code?
want to update this issue with my findings.
I believe the issue i was facing is because of the way java does dns lookup. Java dns lookup for https sites was not going through the upstream corporate web proxy and hence dns lookup resulted in unknownhost exception.
When using a web browser behind a upstream proxy,web browser routes all traffic to upstream web proxy and upstream web proxy does the dns lookup. In case of java, i could not figure out a way to force java to do dns lookup via web proxy.
for now I have a workaround of remphost with its ip address like server.remapHost("www.google.com", "74.125.224.72");
I am going to continue testing this way. If anyone has better idea let know please
Thanks for the update, @mediga. Based on your use case, a couple solutions come to mind:
//Override system DNS setting with Google free DNS server
System.setProperty("sun.net.spi.nameservice.nameservers", "8.8.8.8");
System.setProperty("sun.net.spi.nameservice.provider.1", "dns,sun");
The downside is that you wouldn't be able to resolve internal corporate addresses. It should be possible to configure multiple DNS servers, but I'm not sure how to do it with the JVM implementation.
There's no standard way to determine what the local nameserver or DNS search
path is at runtime from within the JVM. dnsjava attempts several methods
until one succeeds.
- The properties 'dns.server' and 'dns.search' (comma delimited lists) are
checked. The servers can either be IP addresses or hostnames (which are
resolved using Java's built in DNS support).
- The sun.net.dns.ResolverConfiguration class is queried.
- On Unix, /etc/resolv.conf is parsed.
- On Windows, ipconfig/winipcfg is called and its output parsed. This may
fail for non-English versions on Windows.
- As a last resort, "localhost" is used as the nameserver, and the search
path is empty.
You could use one of those listed methods to set dnsjava to use an alternate nameserver, then configure BMP to use dnsjava:
proxy.setHostNameResolver(ClientUtil.createDnsJavaResolver());
thanks @jekh
I tried these options already. from within my corporate firewall/web proxy there is no way to do dns lookup to google dns. The only outgoing connection i can make is a http connection to web proxy and for http sites corp web proxy does the dns lookup. When java does dns lookup it makes a udp call from my machine and this udp call does not go outside the corporate network.
I am experimenting with http api's like whois/statdns which can do a dns lookup for a host and return the ip over an http connection. After the IP is received i use proxy.remaphost to map the domain with the ip adddress.
With this method I got some success and am continuing to test.
Here is the example :
public static String getIPofDomain(String domainName) throws IOException{
String ip=null;
//http://api.statdns.com/facebook.com/a
String protocol = "http";
String host = "api.statdns.com";
String file = "/"+domainName+"/a";
// create a URL object
URL url = new URL(protocol, host, file);
java.net.Proxy proxy = new java.net.Proxy(java.net.Proxy.Type.HTTP, new java.net.InetSocketAddress("127.0.0.1", 8888));
// create the connection object
HttpURLConnection conn = (HttpURLConnection) url.openConnection(proxy);
conn.connect();
// create a stream reader
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String inputLine;
StringBuffer sb = new StringBuffer();
// read contents and print on std out
while ((inputLine = in.readLine()) != null) {
sb.append(inputLine);
System.out.println("line :" +inputLine);
}
Gson gson = new Gson();
JsonObject jsonObject = gson.fromJson( sb.toString(), JsonObject.class);
int arraySize= jsonObject.getAsJsonArray("answer").getAsJsonArray().size();
for(int i=0;i<arraySize;i++){
ip=jsonObject.getAsJsonArray("answer").getAsJsonArray().get(i).getAsJsonObject().get("rdata").getAsString();
System.out.println("ip :"+ ip);
}
//jsonObject.get(fieldName); // returns a JsonElement for that name
return ip;
}
Wow, that is a really clever solution to your problem, @mediga. You could actually implement the HTTP-based lookup as an AdvancedHostResolver and tell BMP to use your code instead of dnsjava or the JVM, and thus eliminate the need to remap hosts.
If you subclass net.lightbody.bmp.proxy.dns.BasicHostResolver
, you can implement the Collection<InetAddress> resolve(String host)
method with the code you pasted above. Then you can have BMP use your resolver by using proxy.setHostNameResolver(...your resolver instance...)
. By default that would result in a new HTTP call for every address, so you may want to add a simple ConcurrentHashMap or guava Cache to your method, to speed things up. But that should work and will be much cleaner than remapping every host.
Thanks @jekh , good idea.
Also I started playing around with httpclient 4.4.1. What i find interesting is making a http/https call from behind corporate proxy using httpclient does not lead to dns lookup for https connections, unlike the way it works in core Java. but rather the https call goes to corporate proxy. Is it possible to swap out the current implementation of https connection mechanism in BMP with httpclient api? If so please guide, i will give it a try.
Example here: public static void main(String[] args)throws Exception { CloseableHttpClient httpclient = HttpClients.createDefault(); try { HttpHost target = new HttpHost("localhost", 443, "https"); HttpHost proxy = new HttpHost("127.0.0.1", 8080, "http");
RequestConfig config = RequestConfig.custom()
.setProxy(proxy)
.build();
HttpGet request = new HttpGet("/");
request.setConfig(config);
System.out.println("Executing request " + request.getRequestLine() + " to " + target + " via " + proxy);
CloseableHttpResponse response = httpclient.execute(target, request);
try {
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
EntityUtils.consume(response.getEntity());
} finally {
response.close();
}
} finally {
httpclient.close();
}
}
In your example code, you're setting the http proxy but not the https proxy, so HttpClient may be using your default proxy settings (i.e. corporate proxy) for HTTPS. But no matter what, you should be able to configure BMP to access your network in exactly the way you want it to. What would you like to do for HTTP and HTTPS requests: have BMP route requests through the corporate proxy, or have BMP attempt direct connections?
This is what will work in my corporate network.
browser <--> bmp <--> charles proxy for ntlm authentication against corp proxy <--> corporate web http proxy does dns lookup <--> internet/intranet sites
only corporate web proxy does the dns lookup for internet sites and intranet sites.
I want BMP/java to route all the http and https requests to corporate proxy and not do a dns lookup by itself.
Hello @jekh , please guide on how should i approach writing the code to achieve the above requirement i have.
I have some good news: it looks like you want to set up BMP with a chained proxy. There is a method in BrowserMobProxy called setChainedProxy(InetSocketAddress chainedProxyAddress) that will allow you to route traffic through an upstream proxy. In your case, you would want to set the chained proxy to the address of the Charles Proxy instance.
There is some bad news though: BrowserMob Proxy cannot intercept HTTPS traffic with a chained proxy. This is due to a limitation in LittleProxy. (I am planning to help address this limitation in LP at some point, but it will not be for at least 1-2 months.) This means you will be able to capture HTTP traffic in a HAR, but not HTTPS.
Alternatively, if you are able to bypass your corporate proxy and charles proxy entirely, you can capture HTTPS traffic in the har, but you will need to find a way to work around the DNS limitations of your corporate network. It sounds like the DNS web service approach that you've implemented might be a good medium-term work-around until LittleProxy can be enhanced with chained proxy MITM support.
@jekh any way to disable dns lookup by bmp and is bmp designed to work without doing dns lookup? Specially for my corporate network scenario where the only way of doing dns lookup is by upstream corporate proxy.
@mediga - when the LittleProxy implementation of BMP is configured with an upstream proxy, there shouldn't be any DNS lookups. If there are, that would be a defect. Do you have a test case or other reproducible example of the LittleProxy-based implementation of BMP causing a DNS lookup when there is a chained proxy? Then I could take a deeper look and fix the defect.
The answer to your first question is yes, you can disable DNS lookups. You could subclass BasicHostResolver and override resolve() to always return an empty list. However, this probably won't give you the results you're looking for, since the proxy would never be able to connect to any non-IP address URLs.
@mediga - BMP now works with MITM+Chained Proxies, as of the latest beta-4 release. So if you configure BMP to use your corporate proxy, it should "just work".
Since there's been no update, I'll close this issue.
Anyone has made BMP work behind the corporate proxy, I am facing a similar issue. Can you please advise ?
I am also in a (my computer) <<---->> (BrowserMob proxy) <<---->> (Corporate Upstream Proxy) <<--->> (internet sites) setting. The chain proxy approach works for me. Thanks everybody for the contribution!
@TTJudy Can you provide a sample implementation for refernce
But the given URL is not opening. Can someone help me with this?
Error
com.google.common.util.concurrent.ExecutionError: java.lang.NoSuchFieldError: id_tc26_signwithdigest_gost_3410_12_256
at com.google.common.cache.LocalCache$Segment.get(LocalCache.java:2216)
at com.google.common.cache.LocalCache.get(LocalCache.java:4147)
at com.google.common.cache.LocalCache$LocalManualCache.get(LocalCache.java:5053)
at net.lightbody.bmp.mitm.manager.ImpersonatingMitmManager.getHostnameImpersonatingSslContext(ImpersonatingMitmManager.java:242)
at net.lightbody.bmp.mitm.manager.ImpersonatingMitmManager.clientSslEngineFor(ImpersonatingMitmManager.java:223)
at org.littleshoot.proxy.impl.ProxyToServerConnection$3.execute(ProxyToServerConnection.java:724)
at org.littleshoot.proxy.impl.ConnectionFlow.doProcessCurrentStep(ConnectionFlow.java:140)
at org.littleshoot.proxy.impl.ConnectionFlow.processCurrentStep(ConnectionFlow.java:128)
at org.littleshoot.proxy.impl.ConnectionFlow.advance(ConnectionFlow.java:90)
at org.littleshoot.proxy.impl.ConnectionFlowStep.onSuccess(ConnectionFlowStep.java:83)
at org.littleshoot.proxy.impl.ConnectionFlow$2.operationComplete(ConnectionFlow.java:149)
at io.netty.util.concurrent.DefaultPromise.notifyListener0(DefaultPromise.java:507)
at io.netty.util.concurrent.DefaultPromise.notifyListenersNow(DefaultPromise.java:481)
at io.netty.util.concurrent.DefaultPromise.notifyListeners(DefaultPromise.java:420)
at io.netty.util.concurrent.DefaultPromise.addListener(DefaultPromise.java:163)
at io.netty.channel.DefaultChannelPromise.addListener(DefaultChannelPromise.java:93)
at io.netty.channel.DefaultChannelPromise.addListener(DefaultChannelPromise.java:28)
at org.littleshoot.proxy.impl.ConnectionFlow.doProcessCurrentStep(ConnectionFlow.java:140)
at org.littleshoot.proxy.impl.ConnectionFlow.access$000(ConnectionFlow.java:14)
at org.littleshoot.proxy.impl.ConnectionFlow$1.run(ConnectionFlow.java:124)
at io.netty.util.concurrent.PromiseTask$RunnableAdapter.call(PromiseTask.java:38)
at io.netty.util.concurrent.PromiseTask.run(PromiseTask.java:73)
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:399)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:464)
at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:131)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.NoSuchFieldError: id_tc26_signwithdigest_gost_3410_12_256
at org.bouncycastle.operator.jcajce.OperatorHelper.
Code
package makemyhar;
import java.net.Inet4Address; import java.net.UnknownHostException; import java.util.Arrays; import java.util.List;
import org.openqa.selenium.Proxy; import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.remote.CapabilityType; import org.openqa.selenium.remote.DesiredCapabilities; import org.testng.Assert; import org.testng.annotations.Test;
import net.lightbody.bmp.BrowserMobProxy; import net.lightbody.bmp.BrowserMobProxyServer; import net.lightbody.bmp.client.ClientUtil; import net.lightbody.bmp.core.har.HarEntry;
public class test {
@Test
public test() throws Exception {
System.setProperty("webdriver.chrome.driver", "/Users/srajendran/Desktop/chromedriver");
DesiredCapabilities capabilities = new DesiredCapabilities();
BrowserMobProxy proxy = getProxyServer(); // getting browsermob proxy
Proxy seleniumProxy = getSeleniumProxy(proxy);
capabilities.setCapability(CapabilityType.PROXY, seleniumProxy);
capabilities.setCapability("chrome.switches", Arrays.asList("--ignore-certificate-errors"));
WebDriver driver = new ChromeDriver(capabilities);
proxy.newHar("test.har"); // creating new HAR
driver.get("http://www.google.com");
List
public Proxy getSeleniumProxy(BrowserMobProxy proxyServer) {
Proxy seleniumProxy = ClientUtil.createSeleniumProxy(proxyServer);
try {
String hostIp = Inet4Address.getLocalHost().getHostAddress();
seleniumProxy.setHttpProxy(hostIp + ":" + proxyServer.getPort());
seleniumProxy.setSslProxy(hostIp + ":" + proxyServer.getPort());
} catch (UnknownHostException e) {
e.printStackTrace();
Assert.fail("invalid Host Address");
}
return seleniumProxy;
}
public BrowserMobProxy getProxyServer() {
BrowserMobProxy proxy = new BrowserMobProxyServer();
proxy.setTrustAllServers(true);
proxy.start();
return proxy;
}
}
I have a very simple scenario of using browser mob behind the corporare proxy. When i try to open http://www.cnn.com things work fine, but if i try to access any secure sites like https://google.com, browsermob fails to get the connection go through.
I have specified the outgoing proxy details and required authentication for successfully making an outgoing connection from behind corporate proxy, as is evident that http://www.cnn.com works fine.
Strangely, this happens when i am at work only. If I am at home and connected to my company network via VPN, everything works fine.
Please help. This issue has become a show stopper for us to further evaluate the usage of Browsermob in the organization