Android Emulator Custom DNS/TLSNick Mooney March 6th, 2019 (Last Updated: March 6th, 2019)
I talked a bit ago about using
mkcert to generate locally-trusted HTTPS certs. This continues to work great! This past week, I was hoping to use those same certs in the Android emulator. You will need to do a couple things in order to get your Android app talking to and trusting your development setup.
In this example, I am running a local instance of a service, and I would like the hostname duotest.service to resolve to 192.168.125.10 (my local machine). Note that I’m using my Wi-Fi IP here instead of localhost, since I need both the Android emulator and my laptop to be able to connect to it. There is some fancy split horizon stuff you could do here to only route locally, but it’s far more complicated than this setup.
I’m going to use dnsmasq as a caching DNS resolver that also respects entries in my /etc/hosts. To set this up:
- Edit /etc/hosts on my development machine to include the line duotest.service 192.168.125.10 (change this when your IP changes)
- Install dnsmasq: brew install dnsmasq
- Edit /usr/local/etc/dnsmasq.conf (see below)
- Start dnsmasq: sudo brew services start dnsmasq
dnsmasq to listen on my Wi-Fi IP, but not localhost (since
dnscrypt-proxy is already listening there). Here are the options I used in
interface=en0 except-interface=lo0 # don't listen on localhost no-dhcp-interface=en0 # don't support DHCP bind-interfaces # don't bind to all interfaces
After starting the service, you should be able to resolve things from your /etc/hosts:
nmooney-22210:~ nmooney$ dig +short @192.168.125.10 duotest.service A 192.168.125.10
When you edit your hosts file, restart dnsmasq with sudo brew services restart
Starting the Android emulator with custom DNS
I have an AVD named “Nexus 5X”. You can no longer specify custom DNS servers or command line options from within Android Studio, but you can use the command line emulator tool to start an Android emulator with custom DNS servers:
nmooney-22210:~ nmooney$ ~/Library/Android/sdk/tools/emulator -avd Nexus_5X -dns-server 192.168.125.10
Note that spaces are replaced with underscores on the command line.
Once you’ve done this and the emulator has booted, you should be able to verify that Android is using your resolver. To test this in my case, I navigated to
https://duotest.service in the Chrome browser within Android. I got a certificate error since I haven’t added my CA yet, but I was able to see that I was being served the
mkcert-generated certificate, which means resolution is succeeding.
First, you’ll need to install your local CA on the emulated Android device. If you’ve followed the mkcert steps, you should be able to run the following to find the location of your cert files:
nmooney-22210:~ nmooney$ mkcert -CAROOT /Users/nmooney/Library/Application Support/mkcert nmooney-22210:~ nmooney$ ls "$(mkcert -CAROOT)" rootCA-key.pem rootCA.pem
rootCA.pem file is what you’ll need to give to the Android emulator.
Installing the CA PEM on Android
- Push the CA file to the device:
adb push rootCA.pem /sdcard/Download/
- On the device, navigate to
Settings -> Network & internet -> Wi-Fi -> Wi-Fi preferences -> Advanced -> Install certificates
- Choose the Downloads folder and click on
- Enter the device PIN, give your CA a name, and hit “OK”
The device now trusts your mkcert CA, and you should be able to confirm this in Chrome by navigating to an HTTPS cite with a mkcert certificate attached to it. At this point in my testing, I was able to visit https://duotest.service/ in Chrome successfully.
Configuring your app to use user CAs
Duo Mobile already has this enabled by default, but in case you’re working on something else, you’ll need to add a
<certificate> entry to
res/xml/network_security_config.xml. A minimal example can be found in Duo Mobile:
<?xml version="1.0" encoding="utf-8"?> <network-security-config> <debug-overrides> <trust-anchors> <certificates src="user" /> </trust-anchors> </debug-overrides> </network-security-config>
This will trust user CAs in debug mode only. You will also need to reference this in your app’s
<?xml version="1.0" encoding="utf-8"?> <manifest ... > <application android:networkSecurityConfig="@xml/network_security_config" ... > ... </application> </manifest>