From 769c197a83096d07ab7c613903a81a287f5155e3 Mon Sep 17 00:00:00 2001 From: Percy Wegmann Date: Thu, 2 Oct 2025 19:37:15 -0500 Subject: [PATCH] configure DNS on macOS runners On macOS, `tailscaled` does not manage DNS. Configure it manually in the GitHub action to make sure MagicDNS name resolution works. Updates tailscale/corp#32821 Signed-off-by: Percy Wegmann --- .github/workflows/test.yml | 21 ++++++++++++++--- README.md | 2 -- dist/index.js | 29 ++++++++++++++++++++++++ dist/logout/index.js | 10 +++++++++ src/logout/logout.ts | 11 +++++++++ src/main.ts | 46 +++++++++++++++++++++++++++++++++++++- 6 files changed, 113 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e501717..81aa8a1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -59,18 +59,26 @@ jobs: arch: arm64 version: latest - # macOS intel + # macOS 13 (AMD64) - os: macos-13 runner-os: macOS arch: amd64 version: latest - ping: 100.99.0.2 # hostnames aren't resolving on MacOS, just ping IP lax-pve.pineapplefish.ts.net,lax-pve + ping: 100.99.0.2,lax-pve.pineapplefish.ts.net,lax-pve - # macOS ARM + # macOS 14 (ARM) - os: macos-14 runner-os: macOS arch: arm64 version: latest + ping: 100.99.0.2,lax-pve.pineapplefish.ts.net,lax-pve + + # macOS latest (ARM) + - os: macos-latest + runner-os: macOS + arch: arm64 + version: latest + ping: 100.99.0.2,lax-pve.pineapplefish.ts.net,lax-pve runs-on: ${{ matrix.os }} @@ -104,6 +112,13 @@ jobs: retry: 3 ping: "${{ matrix.ping }}" + # Look up names to make sure MagicDNS is working + - name: Look up qualified name + run: nslookup lax-pve.pineapplefish.ts.net + + - name: Look up unqualified name + run: nslookup lax-pve + # Test Tailscale status command - name: Check Tailscale Status if: steps.tailscale-oauth.outcome == 'success' diff --git a/README.md b/README.md index dfdec20..dd74fe2 100644 --- a/README.md +++ b/README.md @@ -53,8 +53,6 @@ tailscale ping my-target.my-tailnet.ts.net The `ping` option will wait up to to 3 minutes for a connection (direct or relayed). -> ⚠️ On macOS runners, one can only ping IP addresses, not hostnames. - ## Tailnet Lock If you are using this Action in a [Tailnet diff --git a/dist/index.js b/dist/index.js index bfdcd63..c725ff1 100644 --- a/dist/index.js +++ b/dist/index.js @@ -41244,6 +41244,9 @@ async function run() { core.debug(`Tailscale status: ${JSON.stringify(status)}`); if (status.BackendState === "Running") { core.info("✅ Tailscale is running and connected!"); + if (runnerOS === runnerMacOS) { + await configureDNSOnMacOS(status); + } await pingHostsIfNecessary(config); // Explicitly exit to prevent hanging process.exit(0); @@ -41255,6 +41258,10 @@ async function run() { } catch (err) { core.warning(`Failed to get Tailscale status: ${err}`); + if (runnerOS === runnerMacOS) { + core.setFailed(`❌ Tailscale status is required in order to configure macOS`); + process.exit(2); + } // Still exit successfully since the main connection worked core.info("✅ Tailscale daemon is connected!"); await pingHostsIfNecessary(config); @@ -41750,6 +41757,28 @@ async function installCachedBinaries(toolPath, runnerOS) { } } } +async function configureDNSOnMacOS(status) { + if (!status.CurrentTailnet.MagicDNSEnabled) { + core.info("MagicDNS is disabled, not configuring DNS"); + return; + } + core.info(`Setting system DNS server to 100.100.100.100 and searchdomains to ${status.CurrentTailnet.MagicDNSSuffix}`); + try { + await exec.exec("networksetup", [ + "-setdnsservers", + "Ethernet", + "100.100.100.100", + ]); + await exec.exec("networksetup", [ + "-setsearchdomains", + "Ethernet", + status.CurrentTailnet.MagicDNSSuffix, + ]); + } + catch (e) { + throw Error(`Failed to configure DNS on macOS: ${e}`); + } +} run(); diff --git a/dist/logout/index.js b/dist/logout/index.js index 6cdb6ce..16310d4 100644 --- a/dist/logout/index.js +++ b/dist/logout/index.js @@ -25689,6 +25689,16 @@ const exec = __importStar(__nccwpck_require__(5236)); async function logout() { try { const runnerOS = process.env.RUNNER_OS || ""; + if (runnerOS === "macOS") { + // The below is required to allow GitHub's post job cleanup to complete. + core.info("Resetting DNS settings on macOS"); + await exec.exec("networksetup", ["-setdnsservers", "Ethernet", "Empty"]); + await exec.exec("networksetup", [ + "-setsearchdomains", + "Ethernet", + "Empty", + ]); + } core.info("🔄 Logging out of Tailscale..."); // Check if tailscale is available first try { diff --git a/src/logout/logout.ts b/src/logout/logout.ts index 370932d..8b16a37 100644 --- a/src/logout/logout.ts +++ b/src/logout/logout.ts @@ -8,6 +8,17 @@ async function logout(): Promise { try { const runnerOS = process.env.RUNNER_OS || ""; + if (runnerOS === "macOS") { + // The below is required to allow GitHub's post job cleanup to complete. + core.info("Resetting DNS settings on macOS"); + await exec.exec("networksetup", ["-setdnsservers", "Ethernet", "Empty"]); + await exec.exec("networksetup", [ + "-setsearchdomains", + "Ethernet", + "Empty", + ]); + } + core.info("🔄 Logging out of Tailscale..."); // Check if tailscale is available first diff --git a/src/main.ts b/src/main.ts index 0b7988e..b925986 100644 --- a/src/main.ts +++ b/src/main.ts @@ -47,8 +47,18 @@ interface TailscaleConfig { pingHosts: string[]; } +type tailnetInfo = { + MagicDNSSuffix: string; + MagicDNSEnabled: boolean; +}; + +type tailscaleStatus = { + BackendState: string; + CurrentTailnet: tailnetInfo; +}; + // Cross-platform Tailscale local API status check -async function getTailscaleStatus(): Promise { +async function getTailscaleStatus(): Promise { const platform = os.platform(); if (platform === platformWin32) { @@ -171,6 +181,9 @@ async function run(): Promise { core.debug(`Tailscale status: ${JSON.stringify(status)}`); if (status.BackendState === "Running") { core.info("✅ Tailscale is running and connected!"); + if (runnerOS === runnerMacOS) { + await configureDNSOnMacOS(status); + } await pingHostsIfNecessary(config); // Explicitly exit to prevent hanging process.exit(0); @@ -180,6 +193,12 @@ async function run(): Promise { } } catch (err) { core.warning(`Failed to get Tailscale status: ${err}`); + if (runnerOS === runnerMacOS) { + core.setFailed( + `❌ Tailscale status is required in order to configure macOS` + ); + process.exit(2); + } // Still exit successfully since the main connection worked core.info("✅ Tailscale daemon is connected!"); await pingHostsIfNecessary(config); @@ -799,4 +818,29 @@ async function installCachedBinaries( } } +async function configureDNSOnMacOS(status: tailscaleStatus): Promise { + if (!status.CurrentTailnet.MagicDNSEnabled) { + core.info("MagicDNS is disabled, not configuring DNS"); + return; + } + + core.info( + `Setting system DNS server to 100.100.100.100 and searchdomains to ${status.CurrentTailnet.MagicDNSSuffix}` + ); + try { + await exec.exec("networksetup", [ + "-setdnsservers", + "Ethernet", + "100.100.100.100", + ]); + await exec.exec("networksetup", [ + "-setsearchdomains", + "Ethernet", + status.CurrentTailnet.MagicDNSSuffix, + ]); + } catch (e) { + throw Error(`Failed to configure DNS on macOS: ${e}`); + } +} + run();