
自動化された研究アーティファクト — 上流プロジェクトではありません。
このリポジトリは、ラヴァル大学の修士論文のために、公開されているGitHub Actionsワークフローの脆弱性を再現することを目的として、自動化ハーネスによって構築された使い捨てラボです。これは
serverless-dns/serverless-dnsのコミットb0b1a1538aeb1991b5bc13dfe4e18e686913b12e(2025-04-26)の逐語的なスナップショットであり、そのプロジェクト自身のライセンスの下で再配布されています。そのライセンスファイルはこのスナップショットに変更なしで含まれています。上流プロジェクトは関与しておらず、決して標的にはなりません。ここで研究されている脆弱性はすでに公開されています。このリポジトリ内のすべてのシークレットと変数はランダムに生成されたダミー値であり、実際の認証情報は存在しません。アクションの参照とランナーイメージは、2025-04-26に解決されたものに固定されています。スナップショットに加えられたすべての変更については、ハーネス出力の
pinning.mdを参照してください。質問や異議がある場合: [email protected]
serverless-dns は、Pi-Hole風のコンテンツブロッキング機能を持つ、サーバーレスでスタブ型のDNS-over-HTTPS (DoH) およびDNS-over-TLS (DoT) リゾルバーです。Cloudflare Workers、Deno Deploy、Fastly Compute@Edge、Fly.io ですぐに動作します。これらのサービスの無料枠はすべて、月間10〜20台のデバイス分のDNSトラフィックをカバーするのに十分なはずです。
RethinkDNSは、以下のエンドポイントで本番環境において serverless-dns を実行しています:
サーバー側の処理時間は0ミリ秒(ms)から2ms(中央値)で、エンドツーエンドのレイテンシー(地域やネットワークによって異なります)は10msから30ms(中央値)です。
Fly.io上の Rethink DNS リゾルバーは、FOSS United によってスポンサーされています。
Cloudflare Workersは、serverless-dns をセットアップするのに最も簡単なプラットフォームです:
ステップバイステップの手順については、以下を参照してください:
| プラットフォーム | 難易度 | ランタイム | ドキュメント |
|---|---|---|---|
| ⛅ Cloudflare | 簡単 | v8 Isolates | Cloudflare Workersでのホスティング |
| 🦕 Deno.com | 普通 | Deno Isolates | Deno.comでのホスティング |
| ⏱️ Fastly Compute@Edge | 簡単 | Fastly JS | Fastly Compute@Edgeでのホスティング |
| 🪂 Fly.io | 難しい | Node MicroVM | Fly.ioでのホスティング |
ブロックリストを設定するには、ブラウザから https://<my-domain>.tld/configure にアクセスしてください(RethinkDNSの_設定_ページと同様のものが読み込まれるはずです)。
コード:
# 作業ディレクトリに移動
cd /my/work/dir
# このリポジトリをクローン
git clone https://github.com/serverless-dns/serverless-dns.git
# serverless-dnsに移動
cd ./serverless-dns
Node:
# 必要に応じてnvm経由でnode v22+をインストール
# 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
# 依存関係をダウンロード
npm i
# (任意) 依存関係を更新
npm update
# node上でserverless-dnsを実行
./run n
# clinicjs.orgプロファイラーを実行
./run n [cpu|fn|mem]
Deno:
# deno.land v2+をインストール
# https://github.com/denoland/deno/#install
curl -fsSL https://deno.land/install.sh | sh
# deno上でserverless-dnsを実行
./run d
Fastly:
# 必要に応じてnvm経由でnode v22+をインストール
# Fastly CLIをインストール
# https://developer.fastly.com/learning/tools/cli
# Fastly Compute@Edge上でserverless-dnsを実行
./run f
Wrangler:
# Cloudflare Workers (cli) 別名Wranglerをインストール
# https://developers.cloudflare.com/workers/cli-wrangler/install-update
npm i wrangler --save-dev
# Cloudflare Workers (cli) 上でserverless-dnsを実行
# 最初にWranglerをセットアップしてください:
# https://developers.cloudflare.com/workers/cli-wrangler/authentication
./run w
# Chrome DevToolsでwranglerをプロファイリング
# blog.cloudflare.com/profiling-your-workers-with-wrangler
このリポジトリへのコミットは、Google JavaScriptスタイルガイドを強制します(参照: .eslintrc.cjs)。
.js ファイルに対してリンター(eslint)とフォーマッター(prettier)を実行するgit pre-commit フックがあります。このフックをバイパスするには git commit --no-verify を使用してください。
プルリクエストもコードスタイル違反がないかチェックされ、可能な場合は自動的に修正されます。
デフォルトを調整する必要がある場合は、env.js を設定してください。
Cloudflare Workersの場合は、代わりに wrangler.toml で環境変数を設定してください。
Fastly Compute@Edgeの場合は、代わりに fastly.toml で環境変数を設定してください。
serverless-dnsは、DoHとDoTの両方で英数字ベアラートークンによる認証をサポートしています。トークンについては、msg-key(シークレット)を、hex(hmac-sha256(msg-key|domain.tld), msg) の出力に追加し、ACCESS_KEYS 環境変数にcsv形式で設定します。注: msg は現在 sdns-public-auth-info に固定されています。
msg-key をブロックスタンプの末尾に配置します。例:
1:1:4AIggAABEGAgAA:<msg-key> (ここで 1 はバージョン、1:4AIggAABEGAgAA
はブロックスタンプ、<msg-key> は認証シークレット、: は区切り文字です)。msg-key をブロックスタンプを含むSNI(ドメイン名)の末尾に配置します:
1-4abcbaaaaeigaiaa-<msg-key> (ここで 1 はバージョン、4abcbaaaaeigaiaa
はブロックスタンプ、<msg-key> は認証シークレット、- は区切り文字です)。DoTでも認証を使用する場合は、サブドメインは合計63文字までしか使用できないため、msg-key を短く(8〜24文字)してください。
フォークのアクセスキーは、max.rethinkdns.com から次のように生成できます:
msgkey="ShortAlphanumericSecret"
domain="my-serverless-dns-domain.tld"
curl 'https://max.rethinkdns.com/genaccesskey?key='"$msgkey"'&dom='"$domain"
# 出力
# {"accesskey":["my-serverless-dns-domain.tld|deadbeefd3adb33fa2bb33fd3eadf084beef3b152beefdead49bbb2b33fdead83d3adbeefdeadb33f"],"context":"sdns-public-auth-info"}
serverless-dnsは、Cloudflare Logpush を介してログをアップロードするように設定できます。
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>
# 任意: このワーカーからのログのみがプッシュされるようにフィルターを設定します。ただし、
# Worker名(script-name)でフィルターする必要がない場合は、以下の "filter" フィールドを適宜編集してください。
SCRIPT_NAME=<name-of-the-worker-as-in-wrangler-toml>
# その他のオプションについては、developers.cloudflare.com/logs/get-started/api-configuration を参照
# cURLを使用したLogpush API: developers.cloudflare.com/logs/tutorials/examples/example-logpush-curl
# 利用可能なLogpullフィールド: 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"
}'
wrangler.toml のプロパティ logpush = true を設定します。これにより Logpush が有効になります。LOG_LEVEL = "logpush" を設定します。これにより、リクエスト ログとエラーログのみが出力されるようにログレベルが引き上げられます。LOGPUSH_SRC = "csv,of,subdomains" を設定します。これにより、log-pusher.js は、Workersの hostname がサブドメインのいずれかを含む場合にのみ リクエスト ログを出力します。R2に公開されたログは、R2 Workers、R2 API、またはLogpush APIを使用して取得できます。
Workers Analyticsは、有効にすると、ログキー lid に対してプッシュされます。指定されていない場合、lid はサーバーレスデプロイメントのホスト名に設定され、ピリオド . はアンダースコア _ に置き換えられます。APIを介してAnalyticsを照会する場合は認証を設定する必要があり、jsonを返します。例: https://max.rethinkdns.com/1:<optional-stamp>:<msg-key>/analytics?t=<time-interval-in-mins>&f=<field-name>。可能な fields は、ip(クライアントIP)、qname(DNSクエリ名)、region(リゾルバー地域)、qtype(DNSクエリタイプ)、dom(トップレベルドメイン)、ansip(DNS応答IP)、cc(応答IPの国コード)です。
ログキャプチャと分析は、FlyおよびDeno Deployではまだ実装されていません。
Deno Deploy(クラウド)とDeno(ランタイム)は、同じAPIサーフェスを公開していません(例えば、Deno DeployはHTTP/Sサーバーリスナーのみをサポートします。一方、DenoはプレーンなHTTPおよびHTTP/Sに加えて、生のTCP/UDP/TLSをサポートします)。
Nodeを除き、serverless-dns は環境変数 CF_DNS_RESOLVER_URL / CF_DNS_RESOLVER_URL_2 で定義されたDoHアップストリームを使用します。
Nodeでは、デフォルトのDNSアップストリームは 1.1.1.2(参照)、またはFly.ioで実行する場合は fdaa::3 の再帰的DNSリゾルバーです。
NodeとDenoのエントリーポイントはそれぞれ src/server-node.js、src/server-deno.ts で、
どちらもTCP-over-TLS、HTTP/S接続をリッスンします。一方、HTTP(cli)またはHTTP/S(本番)のみをリッスンするCloudflare Workersのエントリーポイントは src/server-workers.js です。Fastlyの場合は src/server-fastly.js です。
Nodeでのローカル(非本番)セットアップでは、key(秘密)と cert(公開チェーン)ファイルは、デフォルトで環境変数 TLS_KEY_PATH と TLS_CRT_PATH で定義されたパスから読み取られます。
Nodeでの本番セットアップ(Fly.io上)では、TLS_OFFLOAD を true に設定するか、key と cert を環境変数 TLS_CERTKEY で_base64_エンコードする_必要があります_(参照)。例:
# または: tlsをfly.ioにオフロードし、tls_offloadをtrueに設定
TLS_OFFLOAD="true"
# または: key(秘密)とcert(公開チェーン)の両方のbase64表現
TLS_CERTKEY="KEY=b64_key_content\nCRT=b64_cert_content"
Denoの場合、key と cert ファイルは環境変数 TLS_KEY_PATH と TLS_CRT_PATH(参照)で定義されたパスから読み取られます。
プロセスの 起動は、これらの各ランタイムで異なります: Nodeの場合、src/core/node/config.js が_起動_を管理します。
Denoの場合は src/core/deno/config.ts、Workersの場合は src/core/workers/config.js です。
src/system.js のpub-subが、さまざまなモジュール間で_起動_フェーズを調整します。
NodeとDenoでは、プロセス内DNSキャッシュは @serverless-dns/lfu-cache によってバックアップされています。Cloudflare Workersは、Cache Web API とプロセス内lfuキャッシュの両方によってバックアップされています。3つのプラットフォームすべてでキャッシュを完全に無効にするには、環境変数 PROFILE_DNS_RESOLVES=true を設定します。
Cloudflare WorkersとDeno Deployは一時的です。つまり、クライアントリクエストを処理する「プロセス」は長命ではなく、実際、連続する2つのリクエストが異なるisolates(「プロセス」)によって処理される場合があります。Fastly Compute@Edgeも一時的ですが、isolatesを使用せず、代わりにFastlyは各リクエストに対してwasmtimeサンドボックスを作成および破棄します。Fly.io上でNodeを実行しているリゾルバーは、永続的なVMによってバックアップされているため、従来の「serverfull」環境のように長命です。
Deno Deployの場合、コードベースは deno bundle で単一のjavascriptファイルにバンドルされ、Deno.comに引き渡されます。
Cloudflare Workersのビルド時およびランタイム設定は、wrangler.toml で定義されています。
Webpack5はファイルをESMモジュールにバンドルし、その後_Wrangler_によってCloudflareにアップロードされます。
Fastly Compute@Edgeのビルド時およびランタイム設定は、fastly.toml で定義されています。
Webpack5はファイルをESMモジュールにバンドルし、その後 npx js-compute-runtime によってWASMにコンパイルされ、その後_Fastly CLI_でパッケージ化およびFastly Compute@Edgeに公開されます。
Nodeを実行するFly.ioの場合、ランタイムディレクティブはfly.toml(dev および live デプロイメントタイプで使用)で定義され、デプロイディレクティブはnode.Dockerfileにあります。flyctl は、それに応じてFly.ioのインフラストラクチャ上に serverless-dns をセットアップします。
# cloudflare workers.dev用にビルドおよびデプロイ
npm run build
# 通常、env-nameはprodです
npx wrangler publish [-e <env-name>]
# fastly compute@edge用にバンドル、ビルド、デプロイ
# developer.fastly.com/reference/cli/compute/publish
fastly compute publish
# fly.ioにビルドおよびデプロイ
npm run build:fly
flyctl deploy --dockerfile node.Dockerfile --config <fly.toml> [-a <app-name>] [--image-label <some-uniq-label>]
Fly.ioにTLS終端をオフロードするデプロイ(B1 デプロイメントタイプ)の場合、ランタイムディレクティブは代わりにfly.tls.tomlで定義され、ポート 443 でHTTP2 CleartextとHTTP/1.1を、ポート 853 でDNS over TCPをセットアップします。
参照: github/workflows。
190以上のブロックリストは、Succinct Radix Trie(Steve Hanovの実装に基づく)に圧縮され、「簡潔さ」を犠牲にして文字列検索(lookup)を高速化するための変更が加えられています。ブロックリストは、unixタイムスタンプ(pre.sh によってダウンロードされる src/basicconfig.json で定義)でバージョン管理されており、週に1回生成されますが、可能であれば毎日/毎時間生成したいと考えています参照。Cloudflare R2(環境変数: CF_BLOCKLIST_URL)でホストされています。
serverless-dns は、ランタイム起動中にラディックス・トライをセットアップするために必要な3つのブロックリストファイルをダウンロードするか、DNSリクエストを処理するときに遅延ダウンロードします。
serverless-dns は、190以上のブロックリストから約1300万エントリ(2023年1月時点)をコンパイルします。これらはserverless-dns/blocklistsリポジトリで定義されています。