自宅で 7 Days to Die の Dedicated Server を運用しています。
Steamには、ブラウザからゲームサーバーへ接続できる steam://connect というURLがあります。
steam://connect/<IPアドレス>:<ポート>
例えば次のように使えます。
steam://connect/203.0.113.10:26900
便利なのですが、Steamの仕様では現在DNS名を指定できません。
steam://connect/<IP>[:<port>][/<password>]
Note: DNS names no longer work here
自宅サーバーでは固定IPではなく、Dynamic DNSを使っていることがあります。
Dynamic DNSは、変化するグローバルIPアドレスに対して固定のDNS名を割り当てる仕組みです。
そこで、Dynamic DNSをIPアドレスへ変換してから steam://connect にリダイレクトするサービスを Cloudflare Workers で作りました。
作ったもの
サービスはこちらです。
https://connect.steamlink.workers.dev/
URLは次の形式です。
https://connect.steamlink.workers.dev/<DNS名>/<ポート>
例えば、
https://connect.steamlink.workers.dev/example.ddns.net/26900
へアクセスします。
Cloudflare Workersが example.ddns.net を名前解決します。
取得したIPアドレスが 203.0.113.10 なら、次のURLへリダイレクトします。
steam://connect/203.0.113.10:26900
全体の流れは次のとおりです。
これなら自宅のグローバルIPアドレスが変わっても、共有するURLは変更する必要がありません。
Workerのコード
DNSの名前解決には、Cloudflareの DNS over HTTPS を使っています。
DNS over HTTPSは、DNS問い合わせをHTTPSで行う仕組みです。
export default {
async fetch(request) {
const url = new URL(request.url);
const parts = url.pathname.split("/").filter(Boolean);
// /<hostname>/<port>
if (parts.length !== 2) {
return new Response(
"Usage: /<hostname>/<port>\n",
{ status: 400 }
);
}
const hostname = parts[0];
const port = Number(parts[1]);
// hostname validation
if (
hostname.length > 253 ||
!/^[a-zA-Z0-9.-]+$/.test(hostname)
) {
return new Response("Invalid hostname\n", {
status: 400,
});
}
// port validation
if (
!Number.isInteger(port) ||
port < 1 ||
port > 65535
) {
return new Response("Invalid port\n", {
status: 400,
});
}
const dnsUrl =
"https://cloudflare-dns.com/dns-query" +
`?name=${encodeURIComponent(hostname)}&type=A`;
const dnsResponse = await fetch(dnsUrl, {
headers: {
Accept: "application/dns-json",
},
});
if (!dnsResponse.ok) {
return new Response("DNS request failed\n", {
status: 502,
});
}
const dns = await dnsResponse.json();
if (dns.Status !== 0) {
return new Response("DNS lookup failed\n", {
status: 404,
});
}
const record = dns.Answer?.find(
(answer) => answer.type === 1
);
if (!record) {
return new Response("IPv4 address not found\n", {
status: 404,
});
}
const steamUrl =
`steam://connect/${record.data}:${port}`;
return new Response(null, {
status: 302,
headers: {
Location: steamUrl,
"Cache-Control": "no-store",
},
});
},
};
コードは、Cloudflare Workersの管理画面でWorkerを作成して貼り付けるだけです。
サーバーやデータベースは必要ありません。
no-storeを指定する理由
リダイレクトには次のヘッダーを付けています。
Cache-Control: no-store
Dynamic DNSを使う場合、IPアドレスは後から変わる可能性があります。
ブラウザに古いリダイレクト結果を保存されると、変更前のIPアドレスへ接続してしまいます。
そのため、アクセスするたびにDNSを名前解決するようにしています。
7 Days to Dieで使う
例えばDedicated Serverが次の設定だとします。
DNS名: 7dtd.example.net
ポート: 26900
共有するURLはこれだけです。
https://connect.steamlink.workers.dev/7dtd.example.net/26900
DiscordやWebサイトなどにこのURLを貼っておけば、グローバルIPアドレスが変わってもリンクを変更する必要がありません。
ブラウザによっては steam:// を開くときにSteamを起動してよいか確認されます。
まとめ
steam://connect は便利ですが、DNS名を直接指定できません。
Cloudflare Workersを間に入れることで、
という単純な仕組みでDynamic DNSに対応できました。
自宅で 7 Days to Die などのDedicated Serverを動かしている場合には、IPアドレスを意識せず接続URLを共有できるので便利です。