{"id":21631081,"url":"https://github.com/nunum/dns-over-https-nss-linux","last_synced_at":"2025-04-11T14:08:53.133Z","repository":{"id":114413012,"uuid":"308973168","full_name":"NunuM/dns-over-https-nss-linux","owner":"NunuM","description":"DNS over HTTPS on Linux, system-wide","archived":false,"fork":false,"pushed_at":"2024-03-25T22:54:24.000Z","size":46,"stargazers_count":7,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-25T10:12:34.036Z","etag":null,"topics":["dns","doh","libnss","linux"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/NunuM.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2020-10-31T21:15:22.000Z","updated_at":"2024-03-25T22:24:53.000Z","dependencies_parsed_at":null,"dependency_job_id":"85a2086f-dfba-4ffc-a51a-46faf64cdff9","html_url":"https://github.com/NunuM/dns-over-https-nss-linux","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NunuM%2Fdns-over-https-nss-linux","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NunuM%2Fdns-over-https-nss-linux/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NunuM%2Fdns-over-https-nss-linux/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NunuM%2Fdns-over-https-nss-linux/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/NunuM","download_url":"https://codeload.github.com/NunuM/dns-over-https-nss-linux/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248413448,"owners_count":21099309,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dns","doh","libnss","linux"],"created_at":"2024-11-25T02:13:08.775Z","updated_at":"2025-04-11T14:08:53.106Z","avatar_url":"https://github.com/NunuM.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"### DNS over HTTPS on Linux\n\nThis is a library that offers name resolution using [Cloudflare's REST API](https://developers.cloudflare.com/1.1.1.1/dns-over-https) for GLibC based operative systems, DoH for short. In general, every operative system offers some form of DNS name resolution API, for instance, on GLibC you have **gethostbyname**, however, this library goes two steps further, one is the particularity of executing that task over an encrypted channel and the other is that will be the who will provide answers when you call **gethostbyname**.\n\nNowadays, DNS resolution is done via the user data protocol, which implies plaintext queries that are passive to be leaked to third parties capable of monitor the network. The metadata that is being given by us on the intent of which website we are trying to connect to is privacy and a human right offense that we must fight, and as developers, we have a moral responsibility to not accept this situation and push this lack of privacy to **/dev/null**.\n\nWe developers came with two approaches, DNS over TSL (DoT) and DNS over HTTPs traffic (DoH). Both use TLS, one is for encrypts UDP traffic and the other is to encrypt HTTP traffic.\n\nA fast overview of this feature tells that on any distro GNU/Linux with systemd you have DoT, but is disabled by default, on Android DoH was released starting Chrome 80. Apple said that will support DoH and DoT by this fall and Windows 10 also has support for both modes.\n\nI am be focusing on Linux, I won't deny it, it is my favorite. In Linux, we have a special file: **nsswitch.conf** and as [Delorie](https://developers.redhat.com/blog/2018/11/26/etc-nsswitch-conf-non-complexity/) puts out: \"that most people ignore, few people understand, but all people generally rely on\" and I add, and the documentation for developers is scarce.\n\nThis file from the [Name Service Switch](https://www.gnu.org/software/libc/manual/html_node/Name-Service-Switch.html) has the information about the services (databases as they put out) and the corresponding libraries. API calls like **gethostbyname** will trigger a lookup upon this file to see which library to handle the request. In this example, and according to the output of **nss file** the call will be handled by the **files** and dns **database**, if **files** do not come with an answer, the next one will be invoked, in this case, **dns**\n\n```bash\n# cat /etc/nsswitch.conf\n\n# database      implementation\npasswd:         files systemd\ngroup:          files systemd\nshadow:         files\ngshadow:        files\n\nhosts:          files dns\nnetworks:       files\n\nprotocols:      db files\nservices:       db files\nethers:         db files\nrpc:            db files\n\nnetgroup:       nis\n```\n\nTechnically, how this delegation is achieved by GLibC? When you call **gethostbyname**, GLibC selects one library based on your nss configuration, the name of the selected will be used to find the shared library **libnss_dns.so**\nand then, the target method is prefixed **\\_nss_dns_** and a [call](https://www.man7.org/linux/man-pages/man3/dlopen.3.html) is made to the library.\n\n```\n# objdump -d `whereis libnss-dns` | grep '_nss_dns_.*:$'\n0000000000001160 \u003c_nss_dns_gethostbyname3_r@plt\u003e:\n0000000000001220 \u003c_nss_dns_gethostbyname3_r@@GLIBC_PRIVATE-0x12b0\u003e:\n00000000000024d0 \u003c_nss_dns_gethostbyname3_r@@GLIBC_PRIVATE\u003e:\n0000000000002598 \u003c_nss_dns_gethostbyname2_r@@GLIBC_PRIVATE\u003e:\n0000000000002640 \u003c_nss_dns_gethostbyname_r@@GLIBC_PRIVATE\u003e:\n0000000000002728 \u003c_nss_dns_gethostbyname4_r@@GLIBC_PRIVATE\u003e:\n0000000000002a78 \u003c_nss_dns_gethostbyaddr2_r@@GLIBC_PRIVATE\u003e:\n0000000000002e68 \u003c_nss_dns_gethostbyaddr_r@@GLIBC_PRIVATE\u003e:\n00000000000033d8 \u003c_nss_dns_getnetbyname_r@@GLIBC_PRIVATE\u003e:\n0000000000003558 \u003c_nss_dns_getnetbyaddr_r@@GLIBC_PRIVATE\u003e:\n00000000000037f0 \u003c_nss_dns_getcanonname_r@@GLIBC_PRIVATE\u003e:\n```  \n\nThis way, the developers can add their implementation. For each database, the developer must implement the respective interface. Exists several others libraries like **libnss-mysql**, **libnss-systemd**, etc.\n\nAfter showing the internals, by now, we know how to make DoH system-wide a reality. We must implement **hosts API** a\nmake an HTTPs request to CloudFlare's API, and lucky for us, we do not have to implement and make HTTP requests in C since the Rust community is awesome and someone has done the [Rust bindings](https://github.com/csnewman/libnss-rs). The\ninterface becomes simpler.\n\n````rust\npub trait HostHooks {\n    fn get_all_entries() -\u003e Response\u003cVec\u003cHost\u003e\u003e;\n\n    fn get_host_by_name(name: \u0026str, family: AddressFamily) -\u003e Response\u003cHost\u003e;\n\n    fn get_host_by_addr(addr: IpAddr) -\u003e Response\u003cHost\u003e;\n}\n````\n\nThe method **get_host_by_name** is the one that we are interested in. We receive the name and if it is been requested\nIPV4 or IPV6 for that name. Then, we make the [HTTP request](https://github.com/NunuM/dns-over-https-nss-linux/blob/master/doh/src/lib.rs#L76). We have used OpenSSL to encrypt the TPC traffic, on this \nrequest it's added the SNI extension, and I have parsed the HTTP protocol without using any third-party library.\n\nTo install this library, and assuming you already have Rust installed in your machine, open the terminal and type:\n\n```bash\ngit clone https://github.com/NunuM/dns-over-https-nss-linux\n\ncd dns-over-https-nss-linux/doh\n\ncargo build --release\n\ncd ..\n\ncd target/release\ncp libnss_doh.so libnss_doh.so.2\nsudo install -m 0644 libnss_doh.so.2 /lib\nsudo /sbin/ldconfig -n /lib /usr/lib\n\n# edit nss configuration\nsudo nano /etc/nsswitch.conf\n#hosts: files doh \n```\n\nNow, your DNS queries will be handled by this library. Note that if you have your browser open, you need to restart it,\nsince at the time you open your browser the nss configuration was different. \n\nHow do we know that our library is the one making the DNS resolution? Well, you can **ping google.com**, or **strace -o debug ping google.pt** and\nexamine the output of strace command. Or use syslog, by exporting debug variable and **ping** again and search using journalctl command.\n\n```bash\nexport NSS_DOH_DEBUG=1\n\n# see all requests\njournalctl -t nss_doh\n\n# follow last one \njournalctl -f  -t nss_doh\n```\n\nTo conclude, it is obvious that Cloudflare will know our DNS queries, but this is a choice that I am willing to make, so do you. Besides\nthat, this knowledge can be useful for service discovery in distributed systems, or blacklisting/whitelist domain names at the local level. I hope that you like this post\nand if you see any error, am I open to resolve new issues. \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnunum%2Fdns-over-https-nss-linux","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnunum%2Fdns-over-https-nss-linux","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnunum%2Fdns-over-https-nss-linux/lists"}