Inspiration

DNS filtering is an important service for schools, enterprises, ISPs and Wi-Fi providers who need to protect users and networks from undesirable content. For enterprises, custom DNS is also a means to offering employees custom domain names for services (intranet). And at home, a DNS service offers privacy and security. But running a DNS service based on traditional server-oriented software is a barrier for almost everyone.

MyDNS was created to make running custom, private DNS services simple and inexpensive for everyone, at any scale. An additional inspiration to build it out for this Hackathon was doing what's supposed to be impossible: handling UDP with Lambda.

What it does

MyDNS allows anyone with an AWS account to quickly setup a private DNS service with he following capabilities:

  • Traditional UDP-based DNS
  • DNS over HTTPS (DoH) for use in browsers
  • Can block domains (including subdomains) by returning "non-existent domain" responses
  • Can redirect domains by returning a specified IP address (to a Wi-Fi access portal, for example)
  • Can block or redirect IPs owned/operated by a Autonomous System Number (ASN)
  • Optionally, log queries and analyze them to identify suspicious DNS activity (like DNS tunneling)

How we built it

The architecture of MyDNS is entirely serverless. At its core, AWS Lambda functions answer DNS requests via UDP Gateway and (if configured) asynchronously analyze queries. The service is deployed as Infrastructure as Code (IoC) built on CloudFormation and AWS Serverless Application Model (SAM) with light bash scripting.

The Lambda functions are triggered by different event sources:

  • API Gateway for HTTP (DoH) requests
  • Proxylity UDP Gateway for UDP requests
  • EventBridge events when objects are created in S3
  • EventBridge schdule for period updates to external data (IP to ASN mapping0

Challenges we ran into

Directly handling UDP network traffic with Lambda isn't possible using just AWS services. Fortunately, Proxylity UDP Gateway is available in AWS Marketplace and provides a solution in "Listeners" that transform UDP binary packets to JSON and delivers them to serverless resources of our choice (Lambda functions).

Our "suspicious activity" analysis approach required separating subdomains from the full domain name so we could collect statistics on the base domain. As it turns out, that's a whole lot more complicated than we thought! Ultimately we pulled-in a dependency on a library that handles that for the wide variety of special cases.

Getting Chrome to use the DoH endpoint requires a domain with HTTPS. To make the service simple to deploy we decided to make that part of the service optional so it can be setup without having to have a registered domain available. Even with a valid domain in place, undocumented behavior (DoH is still a work in progress) required some trial and error to discover and work around.

DoH is still a work in progress, and understanding exactly how Browsers integrate with it (error scenarios, test requests, etc.) took a little trial and error. As it turns out, blocking the domain "example.com" will cause Firefox to consider a DoH service as nonresponsive -- who knew?

Accomplishments that we're proud of

MyDNS is extremely cost effective compared to traditional, server-based solutions particularly when considering the global, highly available architecture. It has nearly zero cost when not in use, and can scale up to high volume, global workloads when/if needed. The incremental cost of adding multiple regions is zero when the service is idle, though the cost of replicating DDB data across regions does increase the usage based costs.

Providing a simple "opt out" for DNS logging required just a little extra work on the template, but was important given privacy is a common reason for using a private DNS service. We could have just made logging a flag in the code, but we took it a step further and don't even create the related resources when logging is disabled (we use Conditions based on the retention period parameter) .

What we learned

First, we were pleased to find that creating a custom DNS solution using UDP and Lambda is not just possible, but fits very nicely into our serverless development flow. The overall solution has a lot of moving parts to cover DoH as well, but they integrate seamlessly in CloudFormation and at runtime.

While DNS is a simple protocol, there is some nuance to handling it. We decided to use an existing code library to implement the binary parsing and formatting, as well as the client for making upstream DNS calls. That was a great choice.

What's next for MyDNS

MyDNS is available in GitHub and open to pull requests. Some interesting additions we're mulling over are are:

  • An admin user experience to manage blocks and redirects rather than the AWS CLI! :)
  • Enhancing analysis with LLM
  • Time-based domain blocking
  • Oblivious DNS over HTTPS (ODNS) for even more privacy (RFC 9230)
  • You name it!

Built With

Share this project:

Updates