Copyright (c) <2023> apetree1001@email.phoenix.edu
https://github.com/serverless-dns/blocklists serverless stub DNS-over-HTTPS (DoH) and DNS-over-TLS (DoT) resolver Runs out-of-the-box on Cloudflare Workers
Free tiers of all these services
should be enough to cover 10 to 20 devices worth of DNS traffic per month
RethinkDNS resolver RethinkDNS
runs serverless-dns
in production
at these endpoints:
| Cloud platform
| Server locations
|Protocol
| Domain
| Usage |--------------------
| ⛅ Cloudflare Workers
| 280+ (ping) | DoH
| sky.rethinkdns.com
| configure
| 🦕 Deno Deploy
| 30+ (ping) | DoH
| private beta
|
| ⏱️ Fastly Compute@Edge
| 80+ (ping)
| DoH
| private beta
|
|
| 🪂 Fly.io
|30+ (ping) | DoH and DoT | max.rethinkdns.com
| configure |
Server-side processing takes from
0 milliseconds (ms) to 2ms
(median) and end-to-end latency
(varies across regions and networks)
is between 10ms to 30ms (median) Self-host
Cloudflare Workers is the easiest platform to setup
serverless-dns
:
For step-by-step instructions, refer:
| Platform
| Difficulty
| Runtime | Doc |
| ---------------| ---------- | -------------------------------------- | ---------------------------------------------------------------------------------------
| ⛅ Cloudflare
| Easy
| v8 Isolates | Hosting on Cloudflare Workers
|🦕 Deno.com
| Moderate
| Deno Isolates | Hosting on Deno.com
| ⏱️ Fastly Compute@Edge
| Easy
| Fastly JS
| Hosting on Fastly Compute@Edge
| 🪂 Fly.io
| Hard
| Node MicroVM | Hosting on Fly.io
|To setup blocklists visit
https://<my-domain>.tld/configure
from your browser it should load something similar to
RethinkDNS' configure page)
For help or assistance, feel free to open an issue or submit a patch
Setup Code:
navigate to work dir
cd /my/work/dir
# clone this repository
git clone https://github.com/serverless-dns/serverless-dns.git
# navigate to serverless-dns
cd ./serverless-dns
Node:
```bash
# install node v19+ via nvm, if required
# https://github.com/nvm-sh/nvm#installing-and-updating
wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash
nvm install --lts
# download dependencies
npm i
# (optional) update dependencies
npm update
# run serverless-dns on node
./run n
# run a clinicjs.org profiler
./run n [cpu|fn|mem]
Deno:
```bash
# install deno.land v1.22+
# https://github.com/denoland/deno/#install
curl -fsSL https://deno.land/install.sh | sh
# run serverless-dns on deno
./run d
Fastly:
```bash
# install node v18+ via nvm, if required
# install the Fastly CLI
# https://developer.fastly.com/learning/tools/cli
# run serverless-dns on Fastly Compute@Edge
./run f
Wrangler:
```bash
# install Cloudflare Workers (cli) aka Wrangler
# https://developers.cloudflare.com/workers/cli-wrangler/install-update
npm i wrangler --save-dev
# run serverless-dns on Cloudflare Workers (cli)
# Make sure to setup Wrangler first:
# https://developers.cloudflare.com/workers/cli-wrangler/authentication
./run w
# profile wrangler with Chrome DevTools
# blog.cloudflare.com/profiling-your-workers-with-wrangler
Commits on this repository enforces
the Google JavaScript style guide
ref .eslintrc.cjs
A git
pre-commit
hook that runs linter eslint
and formatter prettier on .js
files Use
git commit --no-verify
to bypass this hook
Pull requests are also checked for code style violations and fixed automatically where possible Env vars
Configure
env.js
if you need to tweak the defaults For Cloudflare Workers setup env vars in wrangler.toml
instead
For Fastly Compute@Edge setup env vars
in fastly.toml
instead
Request flow The request/response
flow: client <->src/server-[node|workers|deno]
<-> doh.js
<-> plugin.js
The plugin.js
flow:
user-op.js
->
cache-resolver.js
-> cc.js
-> resolver.js
Auth serverless-dns supports authentication with an
alpha-numeric bearer token for
both DoH and DoT For a token
msg-key
(secret) append the output
of hex(hmac-sha256(msg-key|domain.tld) msg)
to
ACCESS_KEYS
env var in csv format
Note: msg
is currently fixed
to `sdns-public-auth-info
DoH: place the msg-key
at the end of the blockstamp
like so: 1:1:4AIggAABEGAgAA:<msg-key>
here, 1
is the version
1:4AIggAABEGAgAA
is the blockstamp
<msg-key>
is the auth secret
and :
is the delimiter
DoT: place the msg-key
at the end
of the SNI domain-name containing the blockstamp: 1-4abcbaaaaeigaiaa-<msg-key>
(here 1
is the version, 4abcbaaaaeigaiaa
is the blockstamp, <msg-key>
is the auth secret, and -
is the delimeter
If the intention is to use auth with
DoT too keep msg-key
shorter
8 to 24 chars since subdomains may
only be 63 chars long in total You can generate the access keys for your fork from max.rethinkdns.com
like so:
msgkey="ShortAlphanumericSecret"
domain="my-serverless-dns-domain.tld"
curl 'https://max.rethinkdns.com/genaccesskey?key='"$msgkey"'&dom='"$domain"
# output
# {"accesskey":["my-serverless-dns-domain.tld|deadbeefd3adb33fa2bb33fd3eadf084beef3b152beefdead49bbb2b33fdead83d3adbeefdeadb33f"],"context":"sdns-public-auth-info"}
Logs and Analytics serverless-dns can be setup to upload logs via Cloudflare Logpush 0. Setup a Logpush job:
CF_ACCOUNT_ID=<hex-cloudflare-account-id>
CF_API_KEY=<api-key-with-logs-edit-permission-at-account-level>
R2_BUCKET=<r2-bucket-name>
R2_ACCESS_KEY=<r2-access-key-for-the-bucket>
R2_SECRET_KEY=<r2-secret-key-with-read-write-permissions>
# optional, setup a filter such that only logs form this worker ends up being pushed; but if you
# do not need a filter on Worker name (script-name), edit the "filter" field below accordingly.
SCRIPT_NAME=<name-of-the-worker-as-in-wrangler-toml>
for more options, ref: developers.cloudflare.com/logs/get-started/api-configuration
Logpush API with cURL: developers.cloudflare.com/logs/tutorials/examples/example-logpush-curl
Available Logpull fields: developers.cloudflare.com/logs/reference/log-fields/account/workers_trace_events
curl -s -X POST "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/logpush/jobs" \
-H "Authorization: Bearer ${CF_API_KEY}" \
-H 'Content-Type: application/json' \
-d '{
"name": "dns-logpush",
"logpull_options": "fields=EventTimestampMs,Outcome,Logs,ScriptName×tamps=rfc3339",
"destination_conf": "r2://'"$R2_BUCKET"'/{DATE}?access-key-id='"${R2_ACCESS_KEY}"'&secret-access-key='"${R2_SECRET_KEY}"'&account-id='"{$CF_ACCOUNT_ID}"',
"dataset": "workers_trace_events",
"filter": "{\"where\":{\"and\":[{\"key\":\"ScriptName\",\"operator\":\"contains\",\"value\":\"'"${SCRIPT_NAME}"'\"},{\"key\":\"Outcome\",\"operator\":\"eq\",\"value\":\"ok\"}]}}",
"enabled": true,
"frequency": "low"
}' Set `wrangler.toml` property `logpush = true`, which enables
*Logpush* Optional env var `LOG_LEVEL = "logpush"`, which raises the log-level such that only *request* and error logs are emitted Optional Set env var `LOGPUSH_SRC = "csv,of,subdomains"` which makes [`log-pusher.js`](./src/plugins/observability/log-pusher.js) emit *request* logs only if Workers `hostname` contains one of the subdomains Logs published to R2 can
be retrieved either using [R2 Workers](https://developers.cloudflare.com/r2/data-access/workers-api/workers-api-usage), the [R2 API](https://developers.cloudflare.com/r2/data-access/s3-api/api) or the [Logpush API](https://developers.cloudflare.com/logs/r2-log-retrieval) Workers Analytics if enabled, is pushed against a log-key
`lid` which if unspecified is set to hostname of the serverless deployment with periods `.` replaced with underscores `_` Auth must be setup when querying for Analytics via the
API which returns a json
ex:
`https://max.rethinkdns.com/
1 <optional-stamp>:<msg-key>/analytics?t=<time-interval-in-mins>&f=<field-name>` Possible `fields` are `ip` client ip `qname` dns query name `region` resolver region
`qtype` dns query type
`dom` top-level
domains `ansip` dns
answer ips and `cc` ans ip country codes Log capture and analytics isn't yet implemented for Fly and Deno Deploy ***note*** about runtimes Deno Deploy cloud and Deno the runtime
do not expose the same API surface for example Deno Deploy only supports HTTP/S
server-listeners whereas Deno suports raw
TCP/UDP/TLS in addition to plain HTTP and HTTP/S Except on
Node`serverless-dns` uses DoH upstreams defined by env vars
CF_DNS_RESOLVER_URL
/ CF_DNS_RESOLVER_URL_2
On Node
the default DNS upstream is 1.1.1.2
ref or the recursive DNS resolver
at fdaa::3
when running on
Fly.io The entrypoints for Node and
Deno are src/server-node.js
src/server-deno.ts
respectively and both listen for
TCP-over-TLS HTTP/S connections whereas the entrypoint for Cloudflare Workers which only listens over HTTP cli
or over HTTP/S (prod), is src/server-workers.js
and for Fastly its src/server-fastly.js
Local non-prod setups on Node key
private and cert
public chain files
by default are read from paths defined
in env vars TLS_KEY_PATH
andTLS_CRT_PATH
Whilst for prod setup on Node on Fly.io either TLS_OFFLOAD
must be set to true
or key
and cert
must be base64 encoded in env var TLS_CERTKEY
(ref) like so
EITHER: offload tls to fly.io and set tls_offload to true TLS_OFFLOAD="true"
OR: base64 representation of both key private and cert public chain TLS_CERTKEY="KEY=b64_key_content
\nCRT=b64_cert_content"
For Deno, `key` and `cert` files are
read from paths defined in env vars `TLS_KEY_PATH` and `TLS_CRT_PATH`
[ref](https://github.com/serverless-dns/serverless-dns/blob/270d1a3c/src/server-deno.ts#L32-L35) _Process_ bringup is different for each of these runtimes: For
Node [`src/core/node/config.js`](src/core/node/config.js) governs
the _bringup_ while for Deno it
is [`src/core/deno/config.ts`](src/core/deno/config.ts) and for
Workers it is [`src/core/workers/config.js`](src/core/workers/config.js)
[`src/system.js`](src/system.js)
pub-sub co-ordinates the
_bringup_ phase among various modules
On Node and Deno, in-process DNS caching is backed by [`@serverless-dns/lfu-cache`](https://github.com/serverless-dns/lfu-cache); Cloudflare Workers is backed by both [Cache Web API](https://developers.cloudflare.com/workers/runtime-apis/cache) and
in-process lfu caches. To disable caching altogether on all three platfroms, set env var, `PROFILE_DNS_RESOLVES=true` Cloud
Cloudflare Workers and Deno Deploy are ephemeral as in the "process" that
serves client requests is not long-lived
and in fact two back-to-back requests
may be served by two different[_isolates_](https://developers.cloudflare.com/workers/learning/how-workers-works) "processes" Fastly Compute@Edge is the also ephemeral but does not use isolates instead Fastly creates and destroys a
[wasmtime](https://wasmtime.dev/) sandbox for each request Resolver
on Fly.io running Node is backed
by [persistent VMs](https://fly.io/blog/docker-without-docker/) and is hence longer-lived
like traditional "serverful"
environments For Deno Deploy the code-base is bundled up in a single
javascript file with `deno bundle`
and then handed off to Deno.com
Cloudflare Workers build-time and
runtime configurations are defined
in [`wrangler.toml`](wrangler.toml)
[Webpack5 bundles the files](webpack.config.cjs) in an
ESM module which is then uploaded
to Cloudflare by _Wrangler_ Fastly Compute@Edge build-time and runtime configurations are defined
in [`fastly.toml`](fastly.toml)
[Webpack5 bundles the files](webpack.fastly.cjs) in an
ESM module which is then compiled
to WASM by `npx js-compute-runtime`
and subsequently packaged and published to Fastly Compute@Edge with the
_Fastly CLI_
For Fly.io, which runs Node, the runtime directives are defined in [`fly.toml`](fly.toml) (used by `dev` and `live` deployment-types),
while deploy directives are in [`node.Dockerfile`](node.Dockerfile). [`flyctl`](https://fly.io/docs/flyctl) accordingly sets
up `serverless-dns` on Fly.io's infrastructure
build and deploy for
cloudflare workers.dev npm run build
usually, env-name is prod npx wrangler publish [-e <env-name>] bundle build
and deploy for fastly compute@edge
developer.fastly.com/reference/cli/compute/publish fastly compute publish build and deploy to fly.io npm run build:fly
flyctl deploy --dockerfile node.Dockerfile --config <fly.toml>
[-a <app-name>]
[--image-label <some-uniq-label>]
For deploys offloading TLS termination
to Fly.io `B1` deployment-type
the runtime directives are instead defined in [`fly.tls.toml`](fly.tls.toml) which sets up HTTP2 Cleartext and HTTP/1.1 on port `443`, and DNS over
TCP on port `853`
Ref: _[github/workflows](.github/workflows)_ Blocklists 190+ blocklists are compressed in
a _Succinct Radix Trie_
[based on Steve Hanov's impl](https://stevehanov.ca/blog/?id=120) with modifications to speed up string search
[`lookup`](https://github.com/serverless-dns/trie/blob/965007a5c/src/ftrie.js#L378-L484) at the expense of "succintness"
The blocklists are versioned with unix timestamp defined in `src/basicconfig.json` downloaded
by [`pre.sh`](src/build/pre.sh)
which is generated once every week but we'd like to generate them daily / hourly
if possible
[see](https://github.com/serverless-dns/blocklists/issues/19)
and hosted on Cloudflare R2
env var: `CF_BLOCKLIST_URL`
`serverless-dns` downloads
[3 blocklist files](https://github.com/serverless-dns/serverless-dns/blob/15f62846/src/core/node/blocklists.js#L14-L16) required to setup the
radix-trie during runtime bring-up or downloads them
[lazily](https://github.com/serverless-dns/serverless-dns/blob/02f9e5bf/src/plugins/dns-op/resolver.js#L167) when serving a DNS request
`serverless-dns` compiles around
~13M entries as of Jan 2023 from around 190+ blocklists These are defined in
the [serverless-dns/blocklists](https://github.com/serverless-dns/blocklists) repository