DNSCrypt / dnscrypt-proxy

dnscrypt-proxy 2 - A flexible DNS proxy, with support for encrypted DNS protocols.
https://dnscrypt.info
ISC License
11.13k stars 995 forks source link

Documentation about installing on Android #2269

Open emanruse opened 1 year ago

emanruse commented 1 year ago

Here is my experience with 'dnscrypt-proxy' on Android (I use Replicant. Replicant has root and init.d support "out of the box".

To start off, I have spent more than a week digging the web for information on how to do it. I have read many many discussions, questions and answers (including issues here). The official documentation didn't really help. In the very beginning it says:

If you want to change the DNSCrypt resolver, unzip the downloaded archive, edit the RESOLVER_NAME variable in system/etc/init.d/99dnscrypt. Keep the content as a ZIP file, with the original structure.

I have downloaded and unzipped every single release since 2.0.6 (the first one introducing Android binaries). There is no 99dnscrypt in any of them. Neither there is such file in the source code in git. So, my next step was to search the web, read issues here and there, a lot of time and effort.

What I found:

The official documentation also suggests installing one of "four (paid) apps" to switch currently running DNS settings to DNSCrypt. That is another thing I don't want to do. Installing additional (especially non-free) software for the sake of improving security through a software like dnscrypt-proxy, thus increasing attack surface and so on, is a logical contradiction.

So, in my search for a simple, clean, and working FOSS solution I used the same steps as in the Stackexchange question. In my dnscrypt-proxy.toml I enable blocked_names and put only one line in it for testing:

*.fsf.org

My findings:

I notice that 'dnscrypt-proxy' is started twice on boot:

$ adb shell logcat | grep dns
12-29 19:25:23.100  1993  1993 I sysinit : Running /system/etc/init.d/99dnscrypt 
12-29 19:25:23.290  2006  2006 I dnscrypt: Starting dnscrypt-proxy... 
12-29 19:25:23.315  2010  2010 I dnscrypt: Changing dns with iptables... 
12-29 19:27:38.500  3459  3459 I sysinit : Running /system/etc/init.d/99dnscrypt 
12-29 19:27:38.550  3464  3464 I dnscrypt: Starting dnscrypt-proxy... 
12-29 19:27:38.595  3467  3467 I dnscrypt: Changing dns with iptables...

$ adb shell ps | grep dns
root      2009  1     813640 6500  futex_wait 40103db0 S dnscrypt-proxy
root      3466  1     808256 7628  futex_wait 4016fdb0 S dnscrypt-proxy

The 'iptables' rules are not applied:

root@i9300:/ # iptables -L | grep 53                                               
1|root@i9300:/ # iptables -t nat -L | grep 53                                      
1|root@i9300:/ # 

I kill 'dnscrypt-proxy' manually:

$ adb shell
root@i9300:/ # killall dnscrypt-proxy                                              
root@i9300:/ # killall dnscrypt-proxy                                              
killall: dnscrypt-proxy: No such process

Then I start the service manually (cellular data is not enabled):

root@i9300:/ # /etc/init.d/99dnscrypt                                            
root@i9300:/ # [2022-12-29 19:36:20] [NOTICE] dnscrypt-proxy 2.1.2
[2022-12-29 19:36:20] [NOTICE] Network not available yet -- waiting...

Now, 'logcat' and 'ps' show there is only one runnig process:

12-29 19:36:19.860  5209  5209 I dnscrypt: Starting dnscrypt-proxy... 
12-29 19:36:19.970  5213  5213 I dnscrypt: Changing dns with iptables...

$ adb shell ps | grep dns
root      5212  1     812096 7604  futex_wait 401c4db0 S dnscrypt-proxy

The firewall rule is applied correctly too:

root@i9300:/ # iptables -t nat -L | grep 53                                      
DNAT       udp  --  anywhere             anywhere             udp dpt:domain to:127.0.0.1:53

Enable cellular data and watch what is happening in 'adb shell':

root@i9300:/ # [2022-12-29 20:08:31] [NOTICE] Network connectivity detected
[2022-12-29 20:08:31] [NOTICE] Now listening to 127.0.0.1:53 [UDP]
[2022-12-29 20:08:31] [NOTICE] Now listening to 127.0.0.1:53 [TCP]
...
// many other regular notices
...
[2022-12-29 20:09:45] [NOTICE] dnscrypt-proxy is ready - live servers: 32

Everything seems to work. Testing to confirm:

root@i9300:/ # host fsf.org
host: Host not found.
1|root@i9300:/ # host gnu.org
gnu.org has address 209.51.188.116

So, the blocked_names section works and resolving works.

Next, I disabled cellular data, unplugged the USB cable connected to the computer and connected the external WiFi adapter (Replicant needs an external RYF adapter as the phone's built in one won't work without proprietary software). So, I could not test with ADB any more but only using the limited "Terminal" application in Replicant and using the browser that comes with Replicant (no idea what its name is).

In that terminal (rewriting manually here):

host -v fsf.org
host: Host not found.
host -v gnu.org
host: Host not found.

and so on - it seem nothing resolved.

In the browser I see a different result though:

Obviously, things don't work as expected when using WiFi.

Connecting the phone to the computer again I see that 'dnscrypt-proxy' process is still active. Re-testing with cellular data again shows the same result as above: fsf.org is not resolved, other domains are.

The questions are:

  1. Why only the logging commands of the init.d script work, considering that the script is ran by root at boot time?

  2. Why (even with manual starting of the service) the behavior is different when using WiFi?

  3. How to make this service work on boot with all connections (cellular, WiFi, reverse USB tethering, etc) without requiring manual starting through ADB or additional apps (assuming one already has root and init.d support)?

It would be really great if someone can update the documentation.

Best wishes for the new year!

jedisct1 commented 1 year ago

Thanks for your input!

The documentation is a Wiki, so anybody can update it, including you :)

Unfortunately I don't have an Android device, so I have no idea about how this works.

emanruse commented 1 year ago

The documentation is a Wiki, so anybody can update it, including you :)

If I knew the answers to my questions, I could probably do it. Unfortunately, I don't - hence the current issue. As you can see, I have researched quite a lot and didn't find them. So, my last hope is to ask the experts here directly.

Unfortunately I don't have an Android device, so I have no idea about how this works.

Who builds the Android binaries? I suppose they have an Android device and know how to do it.

jedisct1 commented 1 year ago

Android binaries are built automatically by the CI system, like all other binaries.

emanruse commented 1 year ago

But that automation must have been configured by somebody in order to work properly, right? Doesn't that mean that the person who did that understands how Android works?

If I am not asking the right questions at the right place, could you kindly suggest where one can ask the dnscrypt-proxy questions about Android?

jedisct1 commented 1 year ago

But that automation must have been configured by somebody in order to work properly, right? Doesn't that mean that the person who did that understands how Android works?

The beauty of languages such as Go and Zig is that the only thing it takes to build a binary for a given target is to specify the name of the target.

So, that's what I did for different flavors of Linux, Windows, Android, etc. even though I only have a Mac and have no idea how Windows and Android work.

syphyr commented 1 year ago

I did this to add dnscrypt to cm14: https://github.com/syphyr/dnscrypt_proxy_prebuilt

Additional commits:

https://github.com/syphyr/android_packages_apps_Settings/commit/6f4e60eacf8e765704ac35ab869d997fc8541a81

https://github.com/syphyr/android_packages_apps_Settings/commit/5064ace09fef0c89a3af2a7dd5467c47f5cf4d1f

https://github.com/syphyr/android_packages_apps_Settings/commit/786c32c84ad283d43dc9bc6c934bc38a023e6205

https://github.com/syphyr/android_system_sepolicy/commit/580f2ab6dd13895f0fe8024004a68e1c71de7496

https://github.com/syphyr/android_system_sepolicy/commit/11c3cf412f87b690d855d332536e3169f41b73b3

https://github.com/syphyr/android_system_sepolicy/commit/20af525f2d80fa70a877602dab1293af2cd59c71

https://github.com/syphyr/android_system_sepolicy/commit/9717a0fec356dbeb57540fde9411bd4f9b5d6548

To enable:

https://github.com/syphyr/android_vendor_cm-1/commit/8aeb7737ff44830815e9776c44e334e2d51561a

Add to local manifest:

https://github.com/syphyr/cm_build_scripts/commit/59b173830ce78be1bebff6c3d75ba72e9831aceb

MF-Debug commented 1 year ago

@emanruse I suggest you to use Invizible Pro. It's much simpler to use imo.

emanruse commented 1 year ago

@syphyr

OK, I cloned your repo and looked at each and every file in the resulting tree. Now I wonder:

  1. Why did I have to download 1.2 GB and why >99% of that size is in .git directory?

  2. Looking at the essential content, I see 4 files (20Kb total) containing the customizations you made:

.
├── Android.mk
├── dnscrypt
├── dnscrypt-iptables
├── dnscrypt_proxy.rc
└── etc
    └── dnscrypt-proxy // this as this is standard config stuff

Additional commits:

How do I use those? And why?

As you understand, I am definitely not an Android developer. I only know how to do things on Linux. I would really appreciate if you can elaborate on my initial questions, as well as the ones in the current post.

@MF-Debug

I know about this app, thanks. As stated in my initial question #3 - I don't want to install additional apps. Just dnscrypt-proxy.

syphyr commented 1 year ago

@emanruse

1) the repo is large because it is storing prebuilt binaries of dnscrypt-proxy. The older android build environment does not support new versions of go, so I am using prebuilt dnscrypt binaries.

2) getting it to work with my repo will require building the rom. There are required changes to the sepolicy and I added a switch in the developer options to turn dnscrypt off/on and also the ability to enable all traffic through tor. So, those additional commits are for the needed changes to other android repos used to build the rom.

3) I am using root to run dnscrypt because the older version of android (cm-14.1) did not seem to work right when changing the user to anything other than root. Although, I have read people using newer versions of android can change the user to something with less privileges. I couldn't get it to work with other users on cm-14.1, but the added sepolicy domain should help prevent dnscrypt from doing anything it is not supposed to do.

4) "on property" is looking for changes in the android props and performs actions when the props change. That is how the settings work to turn dnscrypt off/on.

5) the method I am using adds dnscrypt as a service to android so that it gets special system privileges and does not get killed under low memory situations, which will happen if you start dnscrypt any other way on android. Using init.d is not the correct way to add a service, that is what the .rc file is for.

6) Android.mk is what the android build system uses to know where to copy the files

emanruse commented 1 year ago

@syphyr

Thank you for this info. I hope you can answer my initial questions too.

Perhaps I didn't stress enough what I mentioned in the initial post, so it seems a good time to do it to avoid further complication and confusion (quoting myself):

So, in my search for a simple, clean, and working FOSS solution

Rebuilding the OS, downloading git-versioned binaries several times bigger than the OS itself, and becoming an Android developer to understand all the intricacies of a complicated process is not suitable for a non-expert.

What I am looking for is something similar to what one does on Linux:

  1. Download the latest release from current repo (without having to use other repos or apps)
  2. Customize config files
  3. Have a working init script for the purpose of dnscrypt-proxy only
  4. Push the proper files to their proper places using adb
  5. Reboot and have everything working as expected

Can you help with that?

syphyr commented 1 year ago

@emanruse

If you look at https://github.com/uzen/dnscrypt-android , this repo will help create a zip file which can be installed from recovery. This is probably the closest you can get to installing dnscrypt-proxy without building the ROM. This repo uses init.d, but it requires a helper application (Afwall) to launch dnscrypt-proxy with root privileges. So, if you use this method, you will need to have root installed on your ROM and you will also need to install Afwall in order to start dnscrypt to get around the sepolicy that would normally prevent dnscrypt from starting up.

syphyr commented 1 year ago

I have created some dnscrypt-proxy installation packages to be installed from the recovery, so you can take a look at the file structure. But in order for this to work, you would also need to update the selinux policy and build your device's boot.img, or this will not work. The selinux policy is contained within your boot.img. Another way to fix the sepolicy is to use Magisk, but I have not tried using that. The changes required to the sepolicy for dnscrypt can be found in my first post.

NOTE: These zip packages do not support installing on A/B file systems

dnscrypt-proxy-arm64-20221231-1-signed.zip: https://www.androidfilehost.com/?fid=4279422670115706958

dnscrypt-proxy-arm-20221231-1-signed.zip https://www.androidfilehost.com/?fid=4279422670115706960

syphyr commented 1 year ago

This is a good reference for launching dnscrypt-proxy at boot: https://android.stackexchange.com/questions/207484/how-to-fix-selinux-avc-denied-errors-when-launching-dnscrypt-as-init-d-script/207647#207647

syphyr commented 1 year ago

BTW, upon further investigation on running dnscrypt-proxy as a non-privileged user, I finally was able to get it working like this: https://github.com/syphyr/dnscrypt_proxy_prebuilt/commit/8e5840185519eed7cf3559d97ea0b9733f653f0c

emanruse commented 1 year ago

@syphyr

Thank you.

If you look at https://github.com/uzen/dnscrypt-android , this repo will help create a zip file which can be installed from recovery.

I have looked. I have linked to that repo in my initial post and shared my experience with it. Regardless of that, could you please explain:

This repo uses init.d, but it requires a helper application (Afwall) to launch dnscrypt-proxy with root privileges. So, if you use this method, you will need to have root installed on your ROM and you will also need to install Afwall in order to start dnscrypt to get around the sepolicy that would normally prevent dnscrypt from starting up.

As per my initial post - I do have root.

Re. Afwall - why should I need that? From what I understand Afwall is pretty much a convenient GUI for handling iptables rules. Considering I have root and can run iptables directly, why should I install other apps? I don' want to install anything except dnscrypt-proxy.

Here are some examples of what the dnscrypt-proxy installation zips look like. These packages contain dnscrypt binaries built from latest source code.

This website requires from me to run proprietary JavaSript. Could you please share the URLs of the actual zip files for direct downloading?

I have created some dnscrypt-proxy installation packages to be installed from the recovery, so you can take a look at the file structure. But in order for this to work, you would also need to update the selinux policy

I have zero experience with selinux administering. Some day I will educate myself on the subject more thoroughly. Or must I do it now? Or is the step required for dnscrypt-proxy simple enough to grasp it from an example?

and build your device's boot.img, or this will not work.

That is a big concern to me. Again, as explained in my initial post, I researched into that and the process of unpacking and repacking the boot.img with no modification whatsoever always results in a smaller image file. During my research, I have read stories about people uploading such "unmodified, just repacked" image back to their device resulting in a boot loop. I don't know if this will happen with my device. What is worse - I don't know how to restore the previous working state of the device if this happens. In short, I am cautious not to damage/brick the device or its OS. Your input on the subject will be much appreciated.

This is a good reference for launching dnscrypt-proxy at boot: https://android.stackexchange.com/questions/207484/how-to-fix-selinux-avc-denied-errors-when-launching-dnscrypt-as-init-d-script/207647#207647

I have shared the same link in my initial post and shared my own result following the steps of the questioner. My result is different. Again - your clarifications are welcome.

BTW, upon further investigation on running dnscrypt-proxy as a non-privileged user, I finally was able to get it working like this:

That's good. I would be glad to try it if I know how. Looking forward to your clarifications!

syphyr commented 1 year ago

Most custom recoveries for android understand a scripting language called Edify. The zip file contains all the files to be installed along with an Edify script that performs actions for the installation such as setting file permissions, etc. You could still do it all manually with adb, but that would get old since you would need to keep doing it over after each rom update. The zip file also contains a script for addon.d which backs up the installation files and restores them automatically on rom updates (just like gapps). Take a look on xda for examples of edify scripts.

Afwall is needed if you want dnscrypt-proxy to start up automatically on bootup. Afwall is capable of starting up automatically on boot and launch a custom script command as root to startup dnscrypt-proxy as well (running as root). If you are using selinux enforcing, then Init.d will not be able to launch dnscrypt-proxy with correct permissions unless additional selinux rules are added to create a new selinux domain for dnscrypt-proxy.

Androidfilehost is a trusted site for android world.

You may want to try a dnscrypt-proxy magisk module if you do not want to build the rom.

emanruse commented 1 year ago

Thank you @syphyr.

So, I have spent a few hours learning about Edify. It's easy for one who has experience with shell script, so no problem with that.

You could still do it all manually with adb, but that would get old since you would need to keep doing it over after each rom update.

That is not a problem for me and I prefer it, if it is possible.

Afwall is needed if you want dnscrypt-proxy to start up automatically on bootup.

I am looking for a way to install and autorun at boot only dnscrypt-proxy (with proper iptables rules) and nothing else (no other apps, no OS rebuilding).

If you are using selinux enforcing, then Init.d will not be able to launch dnscrypt-proxy with correct permissions unless additional selinux rules are added to create a new selinux domain for dnscrypt-proxy.

How do I do that?

Androidfilehost is a trusted site for android world.

I am not arguing. I just need a direct download link, please.

So basically, questions 2 and 3 from my initial post remain unanswered. I hope you can provide actual steps.

dapphp commented 1 year ago

Hey @emanruse ,

I've been running dnscrypt-proxy on Android for 5+ years now and have gone through many iterations with it on various devices and OS's. I had similar preferences to you, wanting to run dnscrypt-proxy on the device with no other dependencies or software.

At the very least, I'd strongly recommend using Magisk to run and manage dnscrypt. I used to roll my own solution (similar to what you're after & documented here) but switched to Magisk a couple of years ago because Universal Init.d would no longer work with newer versions of Android/LineageOS. At first I was hesitant (why run Magisk if I could just make it work with scripts?). After converting my stuff to a Magisk module and using that to manage, install, and run it, I've never been happier with a solution.

Why? Things got difficult to manage. Like the small problems you've encountered, every OS update or at random seemingly out of nowhere, some issue would pop up and I'd have to mess with or tweak things to get it working again. Sometimes I could go weeks without problem switching between cellular and wifi, then reboot one day and nothing was working. This no longer happens with Magisk and the service always runs properly and is easy to restart or debug (using shell scripts) if needed.

In response to your questions...

  1. Why (even with manual starting of the service) the behavior is different when using WiFi?

I haven't run into anything like this for a while so it's hard to say (what OS and device are you using?) but sometimes I'd seen weird behavior where a root shell resolved names differently than a non-root shell, or blocking didn't appear to work from shell but worked fine in a browser. Watch out for IPv6 only DNS resolvers on some wifi or cellular networks. You probably don't have IPv6 rules working (I don't) and the proxy doesn't work for me when there are no IPv4 DNS resolvers.

  1. How to make this service work on boot with all connections (cellular, WiFi, reverse USB tethering, etc) without requiring manual starting through ADB or additional apps (assuming one already has root and init.d support)?

Heavily going to depend on what OS and platform you're using. See my earlier link about Universal Init.d; that was a pretty minimal way that used to work for starting the proxy. Newer Android versions don't support this and Magisk was the easiest way I found for doing this. There are a few basic Magisk dnscrypt modules, or you could easily make your own in an evening or two and I'd be happy to share my code with you as a starting point as well.

Feel free to DM me with questions or post back.

emanruse commented 1 year ago

@dapphp

Thank you for sharing your experience.

I understand your preferences and argumentation.

At the very least, I'd strongly recommend using Magisk to run and manage dnscrypt.

Thank you but I am not looking for that, as explained several times already. Please kindly understand that if I wanted to do this, I would have done it long ago and the current issue would not be opened. I am not simply chasing the easiest possible solution. I am looking for a simple but controlled, open, and verifiable process. What I do on Linux is: install dnscrypt-proxy, set config preferences in /etc/dnscrypt-proxy files and enable the service (which is a simple systemd script). That is the process I am looking for, just for Android.

I have read your article. I don't understand why I would need that "Universal Init.d". I looked at the code of that software and I could not see the necessity for this app, considering that the scripts in my /etc/init.d are already executed on boot. The only problem I am facing with boot time execution is that some of the commands of the init.d script don't seem to have effect, i.e. it appears as though they didn't run. @syphyr explained that this is due to selinux and I still don't know how to fix that. From what I understand, the possible solutions to this are:

Rebuilding the OS has already been discussed and rejected as a simple option. Dealing with the boot.img has been discussed too - I have my big concerns based on experience shared by others, i.e. it looks risky. From what I learned, it seems software like Magisk does exactly that.

The third option (installing a zip in recovery mode) is still not quite clear to me. The Edify scripting is understood. There is some update-binary which seems necessary for it and I still have no idea where this binary comes from, where it source code is and how one can build it oneself. There is also some signing process related to that binary and I still don't know how this works and how one can do it.

Like the small problems you've encountered, every OS update or at random seemingly out of nowhere, some issue would pop up and I'd have to mess with or tweak things to get it working again.

I am not going to update the OS every week or month. As for my problems being small - I don't know, are they? I am not able to start the service automatically on boot. There is no clear documentation on how to do this. Is that a small problem? If it is - that is great. Someone can just simply explain how to do it and we will all have it.

In response to your questions...

  1. Why (even with manual starting of the service) the behavior is different when using WiFi?

I haven't run into anything like this for a while so it's hard to say [...]

But that doesn't really answer the question. I don't use IPv6 resolvers.

what OS and device are you using?

That is answered in the very first sentence of my initial post. I use Replicant. The device is Samsung Galaxy S3 i9300.

  1. How to make this service work on boot with all connections (cellular, WiFi, reverse USB tethering, etc) without requiring manual starting through ADB or additional apps (assuming one already has root and init.d support)?

Heavily going to depend on what OS and platform you're using.

You have that answer, so I hope you can explain.

Feel free to DM me with questions or post back.

Your comments and clarifications here are very welcome!

syphyr commented 1 year ago

Installing dnscrypt-proxy with a flashable zip or manually pushing all files with adb makes no difference when it comes to automatically starting dnscryp-proxy at boot up. You have 3 options without building the ROM.

1) install dnscrypt files any way you like and launch dnscrypt automatically after boot by using another root application (ie; afwall). This will bypass the need to fix selinux rules. Afwall can run additional scripts as root on boot and is not affected by selinux rules.

2) install magisk and use magisk to edit selinux rules which is explained in the link a few posts back. Then you can install dnscrypt manually or use dnscrypt magisk module.

3) install necessary files any way you want and rebuild boot.img with updated sepolicy.

emanruse commented 1 year ago

@syphyr

Yes, I understand there are options involving additional apps, thank you. Those can be included in the updated documentation for anyone willing to use them. There is nothing to discuss in that direction as that is already documented.

Let's please focus on option 3) - could you explain how to do it? What I am still lacking as information is (quoting myself):

A.

There is some update-binary which seems necessary for it and I still have no idea where this binary comes from, where it source code is and how one can build it oneself. There is also some signing process related to that binary and I still don't know how this works and how one can do it.

The context is the directory tree of uzen's zip:

├── META-INF
│   ├── CERT.RSA
│   ├── CERT.SF
│   ├── com
│   │   ├── android
│   │   │   └── otacert
│   │   └── google
│   │       └── android
│   │           ├── update-binary
│   │           └── updater-script
│   └── MANIFEST.MF

B.

That [rebuilding the boot.img] is a big concern to me. Again, as explained in my initial post, I researched into that and the process of unpacking and repacking the boot.img with no modification whatsoever always results in a smaller image file. During my research, I have read stories about people uploading such "unmodified, just repacked" image back to their device resulting in a boot loop. I don't know if this will happen with my device. What is worse - I don't know how to restore the previous working state of the device if this happens. In short, I am cautious not to damage/brick the device or its OS. Your input on the subject will be much appreciated.


C. You say:

updated sepolicy

What exactly needs to be updated for the purpose of running dnscrypt-proxy? How to update it?


To avoid further confusion and need for repeating: I am looking for a manual process without:

Can you help with that?

syphyr commented 1 year ago

@emanruse

If you extract the flashable zip file I posted for you, then inside that file is an edify script called "updater-script". That script should tell you what actions need to be taken on each file. Also, inside that zip is a "system" folder which contains the structure of where each file is copied to.

In regards to patching and building the boot.img, I already posted the required changes for the sepolicy a few posts back (for cm-14.1 branch). The exact sepolicy changes will depend on what version of android you are using, but they are basically the same for all versions of Android. Although, building the boot.img is out of scope for this thread. I suggest looking into setting up an android build environment and searching for build documentation for the rom you are using.

emanruse commented 1 year ago

@syphyr

I think I answered myself to C during my research. Thanks anyway. BTW why do you use 999 as user ID? Android source code shows that AID_NOBODY is 9999. What is 999?

So far, my ongoing research shows that using a zip file has nothing to do with un/mod/re-packing boot.img. Each of the two seems to be a separate way of doing things. With a zip file there is no need to mess up with boot.img. In this case, I suppose we can forget about B. Or, feel free to correct me if I misunderstood.

Re. A - I would appreciate if you re-read the actual question and provide a clear answer. It is the key to installing through a zip file.

syphyr commented 1 year ago

999 is just an undefined AID, which is why the author chose it in his howto. I decided to use a predefined non-privileged user (nobody) instead of an undefined AID. The user "nobody" is commonly used in linux to run daemons as non-privileged processes. Before the dnscrypt-proxy service is started, root executes dnscrypt-cache to make sure dnscrypt-proxy has a cache directory setup with proper permissions set to user nobody. User nobody does not have permission to create it's cache directory on initial setup. Look at https://github.com/syphyr/dnscrypt_proxy_prebuilt/blob/master/dnscrypt_proxy.rc to see how things are started. The android service will start dnscrypt-proxy when "setprop persist.privacy.dnscrypt 1" is executed and stop dnscrypt-proxy when "setprop persist.privacy.dnscrypt 0" is executed.

When using the flashable zip file, you will still need to patch the boot.img. The boot.img contains the sepolicy and the flashable zip does not touch the boot.img. You could just use adb to push the files, set the permissions, chown to proper user/group, and chcon to proper selinux context label (hint: this info is found in the updater-script).

The update-binary is created when building the ROM and often reused to create other flashable zip files. There are lots of tutorials on how to manually sign zip/apk files for android. The zip file is made by basically just compressing a directory with the zip command. Maybe the easiest way for you to start is by using adb and setting it up manually. I suggest to focus on creating a zip file after you get it working.

emanruse commented 1 year ago

@syphyr

I understand what you explain, thank you. What I do not understand is what you do not explain. Meanwhile I also research every day. I see you skip some of my questions (perhaps involuntarily), then I need to repeat them, and this goes on and on. I wonder if it is because the questions are too big to answer shortly. So, to avoid further repeating and to make this easier to follow I will list and enumerate what remains unanswered, making each question as short and specific as possible, so that it is easier to answer and we don't float off again into other things. Additionally, I will also start numbering from 100, to avoid confusion with earlier numbering.

When answering, please let's not forget that the purpose of this issue is to end up with a decent documentation about how one, not being a developer or expert, can do things on one's own, safely and with understanding what one is doing, not merely downloading binaries from here and there, running them blindly, or being told to research all around the web (what I am currently doing).

  1. Why (even with manual starting of the service) the behavior is different when using WiFi? (asked initially)

Earlier you wrote:

4) "on property" is looking for changes in the android props and performs actions when the props change. That is how the settings work to turn dnscrypt off/on.

In your latest reply you also say:

The android service will start dnscrypt-proxy when "setprop persist.privacy.dnscrypt 1" is executed and stop dnscrypt-proxy when "setprop persist.privacy.dnscrypt 0" is executed.

  1. Who/What changes props?
  2. Why should anyone turn dnscrypt off?

There are significant differences between your dnscrypt_proxy.rc and the one from Irfan Latif's answer, given here: https://android.stackexchange.com/questions/207484/how-to-fix-selinux-avc-denied-errors-when-launching-dnscrypt-as-init-d-script/207647#207647

Yours:

service dnscrypt /system/bin/dnscrypt
    class main
    user nobody
    group nobody inet net_raw net_admin
    oneshot
    disabled

His:

service dnscrypt-proxy /system/bin/logwrapper /system/xbin/dnscrypt-proxy -config /system/etc/dnscrypt-proxy/dnscrypt-proxy.toml
    seclabel u:r:dns_crypt:s0
    user 999
    group 999 3003
    disabled
    capabilities NET_BIND_SERVICE
  1. Why does your version lack /system/bin/logwrapper?
  2. What is class main, why do you have it and Irfan does not?
  3. Why does your version lack seclabel u:r:dns_crypt:s0?

Irfan writes that "In order to access internet, process needs to be in group aid_inet (3003) or have the capability NET_RAW (create socket), and should have capability NET_BIND_SERVICE (bind to socket)." Your version uses several groups instead.

  1. Why do you not use capabilities NET_BIND_SERVICE and NET_RAW?

  2. Why do you use groups inet, net_raw and net_admin, considering the later two should be capabilities (not groups)?

  3. What does oneshot do and why you have it and he doesn't?

  4. What means disabled?

  5. Why does your GitHub not contain steps 4 and 5 from Irfan's answer?

In step 4 Irfan writes "That's what works for me on AEX Pie ROM. However the labels and contexts may slightly differ on different Android versions and on different phones."

  1. How can one find out if those really differ on the particular Android version and phone one uses and make the necessary corrections?

  2. What is the way to have universal step 4 (applicable to all Android versions and phones)?

  3. Why do you install your own scripts in /system/bin and not in /system/xbin which is the proper place for non-system executables?

  4. Where is the source code of /META-INF/com/google/android/update-binary?

  5. How to build update-binary from source code?

  6. What are the pros and cons of using something like https://github.com/doppelhelix/android_update-binary_replacement instead of a compiled update-binary?

  7. How to sign the zip?

  8. Where does one get the primary key used to sign the zip?

  9. Where is the source code of the zip signing software?

  10. How to build the zip signing software from source code?

Your system/addon.d/99-dnscrypt-proxy.sh starts with . /tmp/backuptool.functions assuming it is guaranteed to run.

  1. Does backuptool.functions exist in every recovery?

I decided to use a predefined non-privileged user (nobody) instead of an undefined AID.

On Linux I see I have user and group named dnscrypt.

  1. How can we do the same on Android?

When using the flashable zip file, you will still need to patch the boot.img. The boot.img contains the sepolicy and the flashable zip does not touch the boot.img. You could just use adb to push the files, set the permissions, chown to proper user/group, and chcon to proper selinux context label (hint: this info is found in the updater-script).

  1. Please kindly provide actual steps, explaining what each step does, similarly to what Irfan Latif did.
syphyr commented 1 year ago
  1. Why (even with manual starting of the service) the behavior is different when using WiFi? (asked initially) A: I do not have this issue. This depend on your version of android or dnscrypt's iptables script is not setup properly.

  2. Who/What changes props? A: The setprop command or options in Settings

  3. Why should anyone turn dnscrypt off? A: Irrelevant

  4. Why does your version lack /system/bin/logwrapper? A: Its not needed

  5. What is class main, why do you have it and Irfan does not? A: https://android.googlesource.com/platform/system/core/+/master/init/README.md

  6. Why does your version lack seclabel u:r:dns_crypt:s0? A: Because I set selinux file contexts using sepolicy, not in init.

  7. Why do you not use capabilities NET_BIND_SERVICE and NET_RAW? A: Because NET_BIND_SERVICE is only needed when using ports 1-1024. I am using port 5454 for dnscypt-proxy.

  8. Why do you use groups inet, net_raw and net_admin, considering the later two should be capabilities (not groups)? A: Because the version of android I'm using does not support setting capabilities in init.

  9. What does oneshot do and why you have it and he doesn't?

  10. What means disabled? A: https://android.googlesource.com/platform/system/core/+/master/init/README.md

  11. How can one find out if those really differ on the particular Android version and phone one uses and make the necessary corrections? A: Read the sepolicy

  12. What is the way to have universal step 4 (applicable to all Android versions and phones)? A: none

  13. Where is the source code of /META-INF/com/google/android/update-binary? A: Android recovery

  14. How to build update-binary from source code? A: Out of scope for this thread. But don't bother, its irrelevant for getting this working.

  15. What are the pros and cons of using something like https://github.com/doppelhelix/android_update-binary_replacement instead of a compiled update-binary? A: Irrelevant

  16. How to sign the zip?

  17. Where does one get the primary key used to sign the zip?

  18. Where is the source code of the zip signing software?

  19. How to build the zip signing software from source code? A: Google

  20. Does backuptool.functions exist in every recovery? A: No

  21. On Linux I see I have user and group named dnscrypt. How can we do the same on Android? A: You don't. Android is not linux.

  22. Please kindly provide actual steps, explaining what each step does, similarly to what Irfan Latif did. A: This depends on the structure of your ROM and will require research. All I can say is that take a look at updater-script for hints.

ui_print("**********************************************");
ui_print("              DNSCrypt for ARM64              ");
ui_print("**********************************************");

ui_print("Installing files.");
run_program("/sbin/mount", "/system");
run_program("/sbin/mount", "-o", "remount,rw", "/system", "/system");

package_extract_dir("system", "/system");

ui_print("Setting metadata.");
set_metadata_recursive("/system/addon.d", "uid", 0, "gid", 0, "dmode", 0755, "fmode", 0755, "capabilities", 0x0, "selabel", "u:object_r:system_file:s0");
set_metadata_recursive("/system/etc/dnscrypt-proxy", "uid", 0, "gid", 0, "dmode", 0755, "fmode", 0644, "capabilities", 0x0, "selabel", "u:object_r:system_file:s0");
set_metadata("/system/bin/dnscrypt", "uid", 0, "gid", 2000, "mode", 0755, "capabilities", 0x0, "selabel", "u:object_r:dnscrypt_proxy_exec:s0");
set_metadata("/system/bin/dnscrypt-iptables", "uid", 0, "gid", 2000, "mode", 0755, "capabilities", 0x0, "selabel", "u:object_r:dnscrypt_proxy_exec:s0");
set_metadata("/system/etc/init/dnscrypt_proxy.rc", "uid", 0, "gid", 0, "mode", 0644, "capabilities", 0x0, "selabel", "u:object_r:system_file:s0");
set_metadata("/system/xbin/dnscrypt-proxy", "uid", 0, "gid", 2000, "mode", 0755, "capabilities", 0x0, "selabel", "u:object_r:dnscrypt_proxy_exec:s0");

run_program("/sbin/umount", "/system");
ui_print("Done.");
emanruse commented 1 year ago

@syphyr

Thank you for answering some of the questions. I highly value your participation here and your expertise.

Please understand that I spend many hours a day every day to look for these answers, I read hundreds of pages and I am asking about things which I cannot find on the web. This means: if I am asking about something, then I have not been able to find the answer anywhere else after many attempts. So, if you know the answer, please explain, don't send me back to the ocean of [lots of other] information.

  1. Why (even with manual starting of the service) the behavior is different when using WiFi? (asked initially) A: I do not have this issue. This may depends on your version of android or dnscrypt's iptables script is not setup properly.

The question is not "do you have this issue". If it depends, please explain how exactly it depends. I have already shared the info about my Android version and the script.

  1. Why should anyone turn dnscrypt off? A: Irrelevant

Since the possibility to turn dnscrypt off obviously exists, it is obviously relevant to someone. Otherwise the option itself is irrelevant. In either case the answer would not make any sense. Please kindly provide a meaningful answer.

  1. Why do you use groups inet, net_raw and net_admin, considering the later two should be capabilities (not groups)? A: Because the version of android I'm using does not support setting capabilities in init.

108a. How can I check which of the two is supported by Replicant?

  1. How can one find out if those really differ on the particular Android version and phone one uses and make the necessary corrections? A: Read the sepolicy

111a. Where/How?

  1. Where is the source code of /META-INF/com/google/android/update-binary? A: Android recovery

What do you mean? "Android recovery" is a mode of booting the phone. Please be clearer.

  1. How to build update-binary from source code? A: Out of scope for this thread. But don't bother, its irrelevant for getting this working.

Then please explain how to get it working - without building update-binary and without downloading pre-made binaries from here and there. If you can show that update-binary is unnecessary (i.e. the opposite of what was shown so far), then I will agree it is irrelevant. Otherwise - the question remains and is .

  1. What are the pros and cons of using something like https://github.com/doppelhelix/android_update-binary_replacement instead of a compiled update-binary? A: Irrelevant

Not an answer, especially without source code for the binary.

  1. How to sign the zip?
  2. Where does one get the primary key used to sign the zip?
  3. Where is the source code of the zip signing software?
  4. How to build the zip signing software from source code? A: Google

Please. That is not a an answer.

Signing the zip seems necessary. I tried a simple zip (only with "ui_print" instructions in the script) without signing and it would not work. Also, it is not quite clear how to use such zip on Replicant. It seems the zip needs to be provided using adb sideload and I am not sure if this is how it should be done. So, please clarify the process. FWIW, I have read everything on the topic on XDA's forums - there are only premade binaries and jar files there. I found some Java source code but it seems it needs lots of other things. So, it looks complicated.

  1. Please kindly provide actual steps, explaining what each step does, similarly to what Irfan Latif did. A: This depends on the structure of your ROM and will require research. All I can say is that take a look at updater-script for hints.

I have looked. Many times. I am looking for documentation. It obviously does not exist and we are the ones to help with creating it. We should end up with clear steps, not hints. If something is device- or OS version dependent, that should be clarified too in a way which is usable practically.

FWIW, today I tried mkbootimg and unpackbootimg by osm0sis:

https://github.com/osm0sis/mkbootimg

So far, it seems to be the only version which can be easily built from source (in seconds) and most importantly - unpacking and repacking the image results in identical file. The other versions (including the newer Python ones) of these image tools I tried create a repacked image which differs from the original. If you know a better tool, please share.

Earlier you wrote:

You could just use adb to push the files, set the permissions, chown to proper user/group, and chcon to proper selinux context label (hint: this info is found in the updater-script).

I can't find any documentation about chcon. If everything could be done using adb and without any zip, please explain how to do it. Then the questions about sourcing and building the zip-related tools won't matter.

Looking forward to your help. Thanks.

syphyr commented 1 year ago
  1. Why should anyone turn dnscrypt off?

For the same reasons linux users want to be able to start and stop services. Perhaps for testing reasons, perhaps to update a config without having to reboot.

108a. How can I check which of the two is supported by Replicant? Check system/core/init for functions that support the options you want to use. Need to setup a build environment for the ROM to do this.

111a. Where/How? In system/sepolicy, which requires setting up build environment for your ROM.

  1. Where is the source code of /META-INF/com/google/android/update-binary? Android recovery is a github repo. For example, TWRP is found here: https://github.com/omnirom/android_bootable_recovery

107-120: https://developer.android.com/studio/publish/app-signing

  1. Please kindly provide actual steps

I'll provide you the steps for one of the files in updater-script and you should be able to repeat the process for the rest of the files.

(Reboot to recovery first) adb shell mount /system exit adb push dnscrypt /system/bin/ adb shell cd /system/bin chmod 755 dnscrypt chown 0:2000 dnscrypt chcon u:object_r:dnscrypt_proxy_exec:s0 dnscrypt

Note that if you are using my github repo, dnscrypt-cache is not added to updater-script example I posted, but dnscrypt-cache is the exact same procedure for /system/bin/dnscrypt above. Also, the chcon label (u:object_r:dnscrypt_proxy_exec:s0) I used in the example above is found in the selinux changes I posted to create a selinux domain for dnscrypt.

emanruse commented 1 year ago

Thanks.

  1. Where is the source code of /META-INF/com/google/android/update-binary? Android recovery is a github repo. For example, TWRP is found here: https://github.com/omnirom/android_bootable_recovery

I looked at Replicant's recovery repo. There are some references to update binary in one of the .cpp files. However, it doesn't really seem to be the actual source code of update-binary. It seems easier to simply extract the binary from the OS zip file itself:

https://redmine.replicant.us/projects/replicant/wiki/Images#Images

In my case that is:

https://ftp-osl.osuosl.org/pub/replicant/images/replicant-6.0/0004/images/i9300/replicant-6.0-0004-i9300.zip

Then the META-INF contains the necessary binary which is used anyway for the installation of the OS itself.

107-120: https://developer.android.com/studio/publish/app-signing

That link does not contain the answer to these 14 questions. It is just general info about the signing process. It doesn't touch on the following questions, which remain unanswered:

118-120.

I am attempting to answer myself:

In my research I found that software called SignApk is required to sign the zip file. The source code of this app seems to reside here:

https://android.googlesource.com/platform/build/+/refs/heads/master/tools/signapk/

From that, one needs to create a compiled version - a jar file named SignApk.jar. The process requires installing JDK and using commands like:

javac SignApk.java

This is supposed to create an SignApk.class file. Unfortunately, I could not do it. The command outputs many error messages about some dependencies and I am not [planning to become] a Java developer. Then, based on this info:

https://stackoverflow.com/questions/9689793/cant-execute-jar-file-no-main-manifest-attribute

one obviously needs to run a command like:

jar cmvf META-INF/MANIFEST.MF SignApk.jar <path_to>/SignApk.class

As mentioned, I couldn't get to this point, so I have no way to sign a zip without using a pre-made unverifiable binary from somewhere. Unfortunately, as mentioned earlier, Replicant seems to require the sideloaded zip to be signed, so the method of using a zip file without compromising security is blocked for me.

  1. Please kindly provide actual steps

I'll provide you the steps for one of the files in updater-script and you should be able to repeat the process for the rest of the files.

(Reboot to recovery first)

I can push files, change ownership and permissions in regular mode.

123a. Why reboot to recovery to do the same files?

adb push dnscrypt /system/bin/

This line relates to the unanswered: 113.

chcon u:object_r:dnscrypt_proxy_exec:s0 dnscrypt

Please elaborate on this line:

123b. What does it do? 123c. Is it universal for all devices and Android versions? If not - how to make sure it works for the particular device?

Note that if you are using my github repo, dnscrypt-cache is not added to updater-script example I posted, but dnscrypt-cache is the exact same procedure for /system/bin/dnscrypt above. Also, the chcon label (u:object_r:dnscrypt_proxy_exec:s0) I used in the example above is found in the selinux changes I posted to create a selinux domain for dnscrypt.

This is becoming difficult to follow. Since the selinux part seems to be the essence, please provide the consolidated clear and explicit procedure in regards to all that, so that one can follow without wondering.

Also, the steps you outline don't mention the .rc file or boot.img.

123d. Please clarify the steps for customizing and installing dnscrypt_proxy.rc, including unpacking and repacking of boot.img (in case that is necessary).

Here is a new thing I found during my tests:

NO zip, NO boot.img modifications, just this init.d script:

# cat /etc/init.d/99dnscrypt                                          
#!/system/bin/sh

log -p i -t dnscrypt "Changing dns with iptables..."
iptables -t nat -A OUTPUT -p udp --dport 53 -j DNAT --to-destination 127.0.0.1:53 &
log -p i -t dnscrypt "Exit code: $?"

if ! [ "$(pgrep -x dnscrypt-proxy)" ]; then
    log -p i -t dnscrypt "Starting dnscrypt-proxy..."
    dnscrypt-proxy -config /system/etc/dnscrypt-proxy/dnscrypt-proxy.toml &
    log -p i -t dnscrypt "Exit code: $?"
fi
log -p i -t dnscrypt "End of ${0}"

Reboot the device. Then:

$ adb shell logcat | grep dns
01-08 19:00:47.085  1993  1993 I sysinit : Running /system/etc/init.d/99dnscrypt 
01-08 19:00:47.145  1995  1995 I dnscrypt: Changing dns with iptables... 
01-08 19:00:47.160  2000  2000 I dnscrypt: Exit code: 0 
01-08 19:00:47.240  2011  2011 I dnscrypt: Starting dnscrypt-proxy... 
01-08 19:00:47.255  2017  2017 I dnscrypt: Exit code: 0 
01-08 19:00:47.285  2020  2020 I dnscrypt: End of /system/etc/init.d/99dnscrypt 
01-08 19:01:44.800  2364  2695 E mDnsConnector: closing stream for mdns
01-08 19:01:48.425  3392  3392 I sysinit : Running /system/etc/init.d/99dnscrypt 
01-08 19:01:48.455  3398  3398 I dnscrypt: Changing dns with iptables... 
01-08 19:01:48.480  3402  3402 I dnscrypt: Exit code: 0 
01-08 19:01:48.525  3409  3409 I dnscrypt: End of /system/etc/init.d/99dnscrypt 

$ adb shell ps | grep dns
root      2016  1     809544 6292  futex_wait 00000000 S dnscrypt-proxy

IOW, dnscrypt-proxy is started as root on boot. They key points I found in this process:

Based on the above, I tested DNS resolving in 3 modes:

MODE 1: Right after boot, relying only on what the init.d script has done MODE 2: Starting dnscrypt-proxy and running the iptables rule manually MODE 3: Starting dnscrypt-proxy manually but NOT running the iptables rule

Testing with cellular network:

Results:

MODE 1: No resolving whatsoever. MODE 2: Resolving works as expected. MODE 3: Resolving works as expected.

Here is the full output from the tests:

https://susepaste.org/d2026dc5

The identical results from MODE 2 and MODE 3 suggest that the iptables rule is not necessary. I don't know if it is due to the fact that my settings are using port 53 itself and not some other exotic port. This is the same as on my Linux systems - no iptables rules seem necessary. Just like on Linux, on Android I also have:

# cat /etc/resolv.conf                                                                                           
nameserver 127.0.0.1
options edns0

No idea if that is necessary or if it has any effect on Android. I just follow the rules.

Testing with WiFi

  1. (remaining unanswered/unclear, so please comment)

All 3 modes give the same result: Using Replicant's browser I can open 'fsf.org', i.e. it is not blocked as expected. Other domains work too. I don't know how to show any debug info because the only USB port of the device is used by the external USB WiFi adapter which is the only way to have WiFi connection on Replicant without non-free firmware.

Questions based on these recent results:

  1. Why does sysinit run /system/etc/init.d/99dnscrypt twice?
  2. How come dnscrypt-proxy is auto started on boot, considering everything said so far?
  3. Does the identical result of MODE 2 and 3 prove that the iptables rule is not necessary?
  4. Is the iptables rule necessary only in case port other than 53 is used in dnscrypt-proxy config?
  5. Is the /etc/resolv.conf I showed necessary on Android? Does it have any effect at all?
  6. Why in MODE 1 there is no resolving, although the dnscrypt-proxy process is started?
emanruse commented 1 year ago

I tried to answer some of my own questions:

123b. What does it do?

It changes the security context of file dnscrypt to u:object_r:dnscrypt_proxy_exec:s0.

123d. Please clarify the steps for customizing and installing dnscrypt_proxy.rc, including unpacking and repacking of boot.img (in case that is necessary).

This still needs clarification but I will try to fill in some of the gaps with my own findings:

The required procedure seems to include:

https://www.whitewinterwolf.com/posts/2016/08/11/how-to-unpack-and-edit-android-boot-img/

The challenging part is: How to modify /sepolicy. Based on everything so far, there seem to be 3 ways to do this:

I compiled successfully seplicy-inject from https://github.com/xmikos/setools-android. I tested injecting a rule using existing context. Unfortunately, when I try to inject a rule for a context which does not exist, I get this:

$ sepolicy-inject -s dnscrypt -t dnscrypt -c udp_socket -p create -P sepolicy -o sepolicy2
(Android M policy compatibility mode)
libsepol.policydb_index_others: security:  1 users, 2 roles, 577 types, 0 bools
libsepol.policydb_index_others: security: 1 sens, 1024 cats
libsepol.policydb_index_others: security:  87 classes, 5374 rules, 0 cond rules
type dnscrypt does not exist, creating
Segmentation fault (core dumped)

I search the web for 2 hours and could not find a way around this. So, I am stuck here.

  1. Why in MODE 1 there is no resolving, although the dnscrypt-proxy process is started?

I think this may be due to selinux restrictions. I found a way to see them on my version of android (which doesn't show them in logcat):

dmesg | grep avc

I hope you can continue and clarify the rest.

syphyr commented 1 year ago

@emanruse

I noticed that your iptables script does not include TCP, which is incorrect and will definitely cause problems with forwarding dns requests. I suggest you look closer at my repo since I fixed several bugs with iptables.

I also suggest you start with setting up an android build environment and learn how to build the boot.img. You do not have to build the entire ROM. Building only the boot.img is much faster and will solve your selinux issues. Just patch the sepolicy rules and build the boot.img. I do not know anything about using sepolicy-inject, manually repacking the boot.img, or using Magisk to edit the sepolicy.

emanruse commented 1 year ago

@syphyr

I noticed that your iptables script does not include TCP, which is incorrect and will definitely cause problems with forwarding dns requests. I suggest you look closer at my repo since I fixed several bugs with iptables.

I know that. My knowledge may be outdated but what I learned years ago is that DNS works through UDP, so I don't know what problems you mean. Anyway, that is not the central issue at this moment. The important thing now is to get the service working.

I also suggest you start with setting up an android build environment and learn how to build the boot.img. You do not have to build the entire ROM. Building only the boot.img is much faster and will solve your selinux issues. Just patch the sepolicy rules and build the boot.img.

Can you please explain what you mean by "setting up an android build environment" and how to build boot.img only? How do you do it?

I had a look at your .te file but it seems to contain additional rules which I can't relate to anything in Irfan's answer on SO. As a whole - please explain each detail of the selinux changes you make (incl. how do you know the particular rule is necessary) and how to build boot.img. I guess this is the essence of the whole thing we are discussing. Perhaps you can include this explanation in your own repo - that would be beneficial for both projects.

I do not know anything about using sepolicy-inject,

It is a tool which can inject rules directly in the binary sepolicy file. Obviously, that is much easier than rebuilding anything. Why it throws a segmentation fault when attempting to create new context? - I don't know.

manually repacking the boot.img,

https://github.com/osm0sis/mkbootimg https://www.whitewinterwolf.com/posts/2016/08/11/how-to-unpack-and-edit-android-boot-img/

or using Magisk to edit the sepolicy.

That is what Irfan uses in his answer on SO - a tool named supolicy which is part of Magisk.

GNUtoo commented 1 year ago

Hi,

Sorry for the delay (Some questions were also asked on the Replicant mailing list long time ago but I've a huge mail backlog, so I'm really late here).

I don't have a big expertise in running services like that at boot across a wide variety of Android distribution, though I can respond to some of the Replicant 6.0 specific questions.

For SELinux logs, they might be in kernel log (you can get it with 'dmesg' as root), though I didn't check for real as I don't have an easy way to trigger a violation.

That [rebuilding the boot.img] is a big concern to me. Again, as explained in my initial post, I researched into that and the process of unpacking and repacking the boot.img with no modification whatsoever always results in a smaller image file.

During my research, I have read stories about people uploading such "unmodified, just repacked" image back to their device resulting in a boot loop. I don't know if this will happen with my device.

What is worse - I don't know how to restore the previous working state of the device if this happens.

This is relatively easy to do: (1) you need to understand which Replicant version/images you are running and download the corresponding zip file. The Replicant wiki has an article about that if you don't remember the version. (2) The boot.img is inside the zip file. (3) You can restore the original boot.img with this command (this command is specific to the GT-I9300): heimdall flash --BOOT boot.img

  1. How to sign the zip?
  2. Where does one get the primary key used to sign the zip?
  3. Where is the source code of the zip signing software?
  4. How to build the zip signing software from source code?

Basically when building a Replicant 6.0 image, various keys are generated automatically during the build with a script.

You still need to manually enter some data during the first build though if my memory is correct.

One of the generated keys is used to sign the zip. And the ZIPs then checked by the recovery. For instance this enables to reduce the complexity of the installation instruction at the expense of a small increase of security risk.

I can't give you access to the keys as they give the ability to make official Replicant images.

However if you build your own recovery image from the Replicant source code, it will also generate keys that you may be able to reuse to sign zips in the same way that the release zips are signed.

Another option would be to somehow add support in Replicant 6.0 for generating recoveries that don't check signatures, or write some program that somehow disable the signature check of an existing recovery.

I'd need to check (again) how the recovery check the signatures, but if there is an easy way to disable that, a way to go would be to write a script that takes in input a recovery image and produce a modified image without the signature checks.

We already have a program that can add an adb root shell without authentication to the recovery.img or boot.img, for debugging purposes, though it has a big hack: we use sed to change a (boot) property value not to have to unpack and repack the initramfs.

  1. Why should anyone turn dnscrypt off? This way an Android distribution could ship dnscrypt for everyone, and decide in a good default (like have it off or on by default) but also enable people to make the opposite choice (like enable it if it's off).

What do you mean? "Android recovery" is a mode of booting the phone. Please be clearer. It's in the Replicant recovery.img image, so the source code is in Replicant somewhere.

One of the issue is that this source code is huge, so it often takes time to find stuff.

FWIW, today I tried mkbootimg and unpackbootimg by osm0sis:

https://github.com/osm0sis/mkbootimg

So far, it seems to be the only version which can be easily built from source (in seconds) and most importantly - unpacking and repacking the image results in identical file. The other versions (including the newer Python ones) of these image tools I tried create a repacked image which differs from the original. If you know a better tool, please share. Some GNU/Linux distriubtions also packages some of these tools. Parabola and Guix have mkbootimg, and they also have abootimg.

Denis.

GNUtoo commented 1 year ago

Apparently I need a space after the quotes, otherwise it takes the next line in the quote. Because of that some of my answers ended up in the quote.

emanruse commented 1 year ago

@GNUtoo

Thank you for this info. Much appreciated.

From your reply I understand it is possible and fairly easy to restore the original boot.img in case something goes wrong. That is great.

Re. zip signing - I am leaving this aside for the moment because, IIUC, the zip installation is really just an alternative to pushing the necessary files through adb. So, I guess the whole additional complexity of the zip-related process can be skipped.

Now, the essential question here seems to be SELinux, more specifically - the sepolicy file inside boot.img. Obviously, to run dnscrypt-proxy (or any other similar service) one must modify the sepolicy in a way giving the proper permissions. That is understood. The problem is - how does one do this?

So far, in my ongoing research I found there are the following ways:

This means installing Magisk which I don't want to do for security reasons.

Although I was able to build a fork of this tool from source, as mentioned, it cannot the a new domain to sepolicy as required for dnscrypt-proxy service to run:

https://github.com/xmikos/setools-android/issues/17

After digging into that for days, I was unable to make it work. I tried cloning libsepol directly from the SELinux project:

https://github.com/SELinuxProject/selinux

and replacing the files in @xmikos jni/libsepol (which is version 2.7-r4), hoping that a different version of the library may have the issue fixed. Unfortunately, that didn't work either. Newer libsepol versions cause build errors and versions 2.7-r* don't fix the issue. What is more interesting is that libsepol 2.7-r4 from @SELinuxProject is not identical to libsepol 2.7-r4 used in setools-android. Although that same version, taken from SELinux project results in a successful build, the creation of a new context still doesn't work. It shows some other errors. In short, I am hitting a wall here too.

The next option, as suggested by @syphyr:

Although I didn't want to do that, it seems to be the only remaining option. So, I followed the instructions given here:

https://redmine.replicant.us/projects/replicant/wiki/GenericReplicant60Build

In dependencies installation:

https://redmine.replicant.us/projects/replicant/wiki/Replicant60BuildDependenciesInstallation

I see Debian 9 (stretch) - an old, discontinued OS. I used Debian 11 in a VM. The repo sync took many many hours - a whole 34 GiB were downloaded. Unfortunately, some of the dependency packages don't seem to exist on Debian 11: eclipse-jdt, libandroidsdk-ddmlib-java, libandroidsdk-sdklib-java and one more. Also, there is no 'llvm-defaults', there is llvm. I think there were some other differences too. The given commands also don't work literally - I had to install the packages one by one. Pasting the whole lines from the doc didn't work. Anyway, I tried my best to set up this environment.

Then, in step 'Building the toolchain':

https://redmine.replicant.us/projects/replicant/wiki/GenericReplicant60Build#Building-the-toolchain

I ran:

./vendor/replicant/build-toolchain

The process starts but fails with a linker error somewhere at 4% (something related to LLVM, IIRC). I looked all over the web for this error but couldn't find a way to fix it. The "self-contained version or repo" didn't work either. Even make clobber, make clean and make tcclean don't work (errors).

Is the documentation for building Replicant outdated? I don't know. The fact is, it is not possible to simply follow the instruction and get a working build on an up-to-date OS. Additionally, it is not clear how to build just the boot.img as @syphyr suggests.

So, I have exhausted every possible option I could find. Nothing works.

Since opening this issue on 29.Dec.2022, I have been researching and trying for many hours each and every day, without exception, gathering and correlating info scattered all over the web. There is no clarity and completeness. For an expert, this may be easy but for a non-developer like me, it is a tremendous effort without success.

My last hope is someone who has the expertise to kindly provide proper, working steps to modify sepolicy correctly.

emanruse commented 1 year ago

@syphyr

Can you help?

syphyr commented 1 year ago

@emanruse

I've never built Replicant, but most likely the toolchain is prebuilt when you setup the android build environment. I've never had to build the toolchain when compiling a ROM.

Use the recommended version of Linux when setting up your Android build environment. If you try to use a new version of Linux, there will most likely be conflicts and problems.

Building the boot.img is as simple as "mka bootimage" when building AOSP. I am unfamiliar with Replicant sources but I would assume it is very similar to AOSP.

Perhaps there is a help channel for Replicant development that can assist you with the details of building their ROM.

emanruse commented 1 year ago

Thanks @syphyr.

Use the recommended version of Linux when setting up your Android build environment.

It is not recommended. I don't know if a discontinued release can even be considered as a recommendation. It is just mentioned in the build docs as one of the two options (the second one is documented as unconfirmed and most likely not working). Although it is possible to download and install Debian 9 (which I have done too), I have no experience with Debian and I could not find info whether it is even possible to install packages on a discontinued release, i.e. whether the repo of the release is functional at all. So, this whole thing becomes an academic quest quite far away from dnscrypt-proxy.

Perhaps there is a help channel for Replicant development that can assist you with the details of building their ROM.

I have used all available channels but it seems there isn't significant activity in that community. Latest topics have answers from many months and years ago.

But let's please focus on sepolicy. I understand that this must be modified. Is it possible to somehow rebuild only the sepolicy itself (not the whole OS or the boot.img)? IOW, is it possible to somehow "decompile", modify and "recompile" the sepolicy file?

Could you also answer the previously asked:

I had a look at your .te file but it seems to contain additional rules which I can't relate to anything in Irfan's answer on SO. As a whole - please explain each detail of the selinux changes you make (incl. how do you know the particular rule is necessary) [...] Perhaps you can include this explanation in your own repo - that would be beneficial for both projects.

syphyr commented 1 year ago

@emanruse

As you have already mentioned, there are tools to manually unpack/repack the boot.img, but I do not have any knowledge of changing the sepolicy without building the boot.img from source. Perhaps there is something on XDA that would help?

As for creating the rules on the .te file, this is done by reading the selinux denial errors in dmesg and fixing each error until they are gone. Tools like audit2allow can be useful for translating the selinux errors from dmesg. This is a good guide for fixing selinux denials: https://source.android.com/docs/security/features/selinux/validate

emanruse commented 1 year ago

Thanks @syphyr.

What Linux distro and which release do you use to build the boot.img (for CM 14.1 which you say you use)? What is your complete procedure regarding that?

Perhaps there is something on XDA that would help?

Only old threads. I have read them all and the info from there is summarized in my previous reply here.

As for creating the rules on the .te file, this is done by reading the selinux denial errors in dmesg and fixing each error until they are gone.

Are you saying that what you did was:

a. notice a denial error b. add a rule to fix it c. rebuild boot.img g. repeat a-c until there are no more errors?

syphyr commented 1 year ago

@emanruse

I used Google's official documentation on how to setup an android build environment. Google uses Ubuntu. The cm-14.1 branch (LineageOS) that I am using recommends Ubuntu 18.04 Bionic. You will have to see if your device is supported by LineageOS, perhaps only on an older branch.

Are you saying that what you did was:

Correct.

emanruse commented 1 year ago

@syphyr

I finally found the time to return to this. I worked to set up a build environment as per your advice. I have built successfully the original full Replicant OS, as well as just the boot.img.

Could you advise on the specific steps which are necessary to modify the sepolicy for the purpose of running the dnscrypt-proxy service? Which files do I have to modify and how before rebuilding?

syphyr commented 1 year ago

Could you advise on the specific steps which are necessary to modify the sepolicy for the purpose of running the dnscrypt-proxy service? Which files do I have to modify and how before rebuilding?

I'm not familiar with Replicant OS, but the changes to selinux that I gave is for Android 7 (Nougat). It should not be too difficult to use the selinux changes I posted as an example and port them to your specific OS.

1alexman1 commented 1 year ago

Why dnscrypt cannot be found in Fdroid? Are you intend to do it?Thx

karolyi commented 6 months ago

FWIW: https://github.com/DNSCrypt/dnscrypt-proxy/discussions/2328#discussioncomment-8084697

emanruse commented 6 months ago

I know about that InviZible app. I wonder how much it can be trusted long term though (considering app updates too).

I am still planning to continue fighting the current issue. Just paused for a long time due to other unrelated things.

karolyi commented 6 months ago

You can install it from F-Droid which means it's open source and can be maintained even after its current maintainer stops.

But in the end, it's up to you.

elovin commented 5 months ago

I use the arm64 build with lineageOS 20 (Android 13) at the moment.

Although I run the service as root on system start without SELINUX which is fine for me since I do not do online banking on my phone.

In case it helps someone here is the link to the repo: https://github.com/elovin/lineageos-clean-browsing