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 <percy@tailscale.com>
This commit is contained in:
Percy Wegmann 2025-10-02 19:37:15 -05:00 committed by Percy Wegmann
parent d4729ea54c
commit 2c45c8a0fd
6 changed files with 113 additions and 6 deletions

View file

@ -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'

View file

@ -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

29
dist/index.js vendored
View file

@ -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();

10
dist/logout/index.js vendored
View file

@ -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 {

View file

@ -8,6 +8,17 @@ async function logout(): Promise<void> {
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

View file

@ -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<any> {
async function getTailscaleStatus(): Promise<tailscaleStatus> {
const platform = os.platform();
if (platform === platformWin32) {
@ -171,6 +181,9 @@ async function run(): Promise<void> {
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<void> {
}
} 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<void> {
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();