From 57a53e1fde8e9a02e4f3621cd0f9de03ab11fc90 Mon Sep 17 00:00:00 2001 From: Damien Coles Date: Thu, 12 Feb 2026 15:42:23 -0800 Subject: [PATCH] fix domain auth at login: DNS persistence via scheduled task Nebula recreates the nebula1 TUN adapter on every start, wiping DNS settings. This caused domain authentication to fail at the Windows login screen because Netlogon could not reach the DC. Changes: - install-nebula.ps1 now takes -DnsServer and -Domain parameters - Changed service start type from delayed-auto to auto - Creates set-dns-on-start.ps1 startup script and NebulaDNS scheduled task - Sets ExpectedDialupDelay=60 in Netlogon registry - Idempotency check verifies scheduled task and startup script exist --- CHANGELOG.md | 10 ++++++ README.md | 25 ++++++++++---- bootstrap.ps1 | 2 +- install-nebula.ps1 | 83 +++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 109 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d75b232..b53039b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## 1.1.0 + +DNS persistence and domain authentication at the login screen. + +- `install-nebula.ps1` now requires `-DnsServer` and `-Domain` parameters +- Nebula service changed from `delayed-auto` to `auto` start — NlaSvc dependency already ensures the physical network is up +- Creates a `NebulaDNS` scheduled task that runs at startup to re-apply DNS on the `nebula1` adapter after Nebula recreates it, wait for the DC to become reachable, and force Netlogon DC rediscovery via `nltest` +- Sets `ExpectedDialupDelay` (60 seconds) in the Netlogon registry to give the tunnel time to establish before Netlogon gives up +- Idempotency check now also verifies the scheduled task and startup script exist + ## 1.0.0 Initial release. diff --git a/README.md b/README.md index 7d31233..4653a76 100644 --- a/README.md +++ b/README.md @@ -55,9 +55,9 @@ If you need to run steps individually: ```powershell # 1. Install Nebula -& "C:\path\to\nebula\install-nebula.ps1" +& "C:\path\to\nebula\install-nebula.ps1" -DnsServer 10.10.10.13 -Domain "arvandor.internal" -# 2. Point DNS at the domain controller +# 2. Point DNS at the domain controller (for the current session) & "C:\path\to\nebula\set-dns.ps1" -DnsServer 10.10.10.13 # 3. Join domain @@ -66,6 +66,10 @@ If you need to run steps individually: ### Parameters +**install-nebula.ps1** +- `-DnsServer` (required) — IP address of the domain controller. Used to configure DNS persistence across reboots. +- `-Domain` (required) — FQDN of the Active Directory domain. Used to configure Netlogon DC rediscovery at startup. + **set-dns.ps1** - `-DnsServer` (required) — IP address of the domain controller - `-InterfaceAlias` (optional) — Target a specific adapter. Defaults to `nebula1` @@ -90,14 +94,16 @@ If you need to run steps individually: All scripts are safe to re-run: -- `install-nebula.ps1` — Skips reinstall if the service is already running with the correct version and matching files. Tears down and rebuilds if the version or files have changed. +- `install-nebula.ps1` — Skips reinstall if the service is already running with the correct version, matching files, and the `NebulaDNS` scheduled task exists. Tears down and rebuilds if the version or files have changed. - `set-dns.ps1` — Overwrites the existing DNS setting on the Nebula adapter. - `join-domain.ps1` — Skips the join if already on the domain. ## Architecture -- **Nebula** provides the encrypted mesh network. It runs as a `LocalSystem` service with `delayed-auto` start, depending on `Tcpip` and `NlaSvc` (Network Location Awareness). This ensures the physical network is connected before Nebula attempts handshakes. +- **Nebula** provides the encrypted mesh network. It runs as a `LocalSystem` service with `auto` start, depending on `Tcpip` and `NlaSvc` (Network Location Awareness). This ensures the physical network is connected before Nebula attempts handshakes. - **DNS** is configured only on the `nebula1` adapter. Physical adapters (Wi-Fi, Ethernet) keep their DHCP-assigned DNS so they can reach the internet and Nebula lighthouses independently. +- **DNS persistence** — Nebula recreates the `nebula1` TUN adapter on every start, which wipes DNS settings. A `NebulaDNS` scheduled task runs at startup to re-apply DNS on the adapter, wait for the domain controller to become reachable, then force Netlogon to rediscover the DC via `nltest`. This ensures domain authentication works at the Windows login screen before any user logs in. +- **Netlogon tuning** — `ExpectedDialupDelay` (60 seconds) is set in the registry to give Netlogon additional time to locate the DC over the Nebula tunnel, which may involve relay handshakes. - **Domain trust** flows through the Nebula tunnel. The domain controller is reachable at its Nebula IP, so machines do not need a VPN or physical proximity to the DC. ## Troubleshooting @@ -119,11 +125,11 @@ Common causes: - **"no config files found"** — The service was installed without `-config`. Reinstall using `install-nebula.ps1`. ### Handshake timeouts after reboot -Nebula started before the physical network was ready. Verify the service is configured with delayed start and NLA dependency: +Nebula started before the physical network was ready. Verify the service is configured with auto start and NLA dependency: ```powershell sc qc nebula ``` -Expected: `START_TYPE: AUTO_START (DELAYED)` with `DEPENDENCIES: Tcpip, NlaSvc`. Re-run `install-nebula.ps1` to fix. +Expected: `START_TYPE: AUTO_START` with `DEPENDENCIES: Tcpip, NlaSvc`. Re-run `install-nebula.ps1` to fix. ### DNS resolution fails Verify DNS is set on the Nebula adapter only: @@ -147,6 +153,13 @@ The Nebula tunnel must be established before Windows attempts domain authenticat 1. Nebula service is running: `sc query nebula` 2. Tunnel is active: `ping 10.10.10.13` 3. DNS resolves: `nslookup arvandor.internal` +4. NebulaDNS scheduled task exists: `Get-ScheduledTask -TaskName "NebulaDNS"` +5. Startup script exists: `Test-Path "C:\Program Files\Nebula\set-dns-on-start.ps1"` + +If DNS is not set on `nebula1` after a reboot, the scheduled task may have failed. Check its last run result: +```powershell +Get-ScheduledTaskInfo -TaskName "NebulaDNS" | Select-Object LastRunTime, LastTaskResult +``` If the service is running but handshakes fail, check that the machine's Nebula certificate group has firewall access to the `ad` group. diff --git a/bootstrap.ps1 b/bootstrap.ps1 index 5f8ab8e..4a86f64 100644 --- a/bootstrap.ps1 +++ b/bootstrap.ps1 @@ -27,7 +27,7 @@ $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Definition Write-Host "" Write-Host "=== Step 1/3: Installing Nebula ===" -& "$ScriptDir\install-nebula.ps1" +& "$ScriptDir\install-nebula.ps1" -DnsServer $DnsServer -Domain $Domain if ($LASTEXITCODE -ne 0) { exit 1 } # --- Step 2: Set DNS --- diff --git a/install-nebula.ps1 b/install-nebula.ps1 index a127791..3973d4f 100644 --- a/install-nebula.ps1 +++ b/install-nebula.ps1 @@ -1,5 +1,13 @@ #Requires -RunAsAdministrator +param( + [Parameter(Mandatory=$true)] + [string]$DnsServer, + + [Parameter(Mandatory=$true)] + [string]$Domain +) + $ErrorActionPreference = "Stop" # --- Configuration --- @@ -18,10 +26,14 @@ if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administra $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Definition $InstallDir = "C:\Program Files\Nebula" $ConfigPath = "$InstallDir\config.yml" +$StartupScript = "$InstallDir\set-dns-on-start.ps1" $ExpectedBinPath = "`"$InstallDir\nebula.exe`" -service run -config `"$ConfigPath`"" $TempZip = Join-Path $env:TEMP "nebula-windows-amd64.zip" $TempExtract = Join-Path $env:TEMP "nebula-extract" +# Derive the NetBIOS domain name from the FQDN (first label, uppercased) +$NetBIOSDomain = ($Domain -split '\.')[0].ToUpper() + # --- Validate per-host files before making any changes --- $missing = @() @@ -69,7 +81,11 @@ if ($existing) { } } - if ($allMatch) { + # Also verify the scheduled task and startup script exist + $taskExists = Get-ScheduledTask -TaskName "NebulaDNS" -ErrorAction SilentlyContinue + $scriptExists = Test-Path $StartupScript + + if ($allMatch -and $taskExists -and $scriptExists) { Write-Host "Nebula $NebulaVersion is already installed and running with matching files. No changes needed." exit 0 } @@ -195,10 +211,10 @@ if ($LASTEXITCODE -ne 0) { exit 1 } -# Delayed start gives Wi-Fi/Ethernet time to fully associate and get an IP -sc.exe config nebula start= delayed-auto | Out-Null +# Auto start — the NlaSvc dependency ensures the physical network is up first +sc.exe config nebula start= auto | Out-Null if ($LASTEXITCODE -ne 0) { - Write-Error "Failed to set delayed-auto start." + Write-Error "Failed to set auto start." exit 1 } @@ -220,3 +236,62 @@ if ($svc.Status -eq "Running") { Write-Error "Nebula service failed to start. Check Event Viewer (Application log, source 'nebula')." exit 1 } + +# --- Configure DNS persistence across reboots --- +# +# Nebula recreates the nebula1 TUN adapter on every start, which wipes any +# DNS settings. This scheduled task runs at startup to re-apply DNS, wait +# for the domain controller to be reachable, then force Netlogon to +# rediscover the DC — ensuring domain authentication works at the login screen. + +Write-Host "Creating startup DNS script..." +$startupScriptContent = @" +# Wait up to 120 seconds for the nebula1 adapter to appear +`$timeout = 120 +while (`$timeout -gt 0) { + `$adapter = Get-NetAdapter -Name "nebula1" -ErrorAction SilentlyContinue + if (`$adapter -and `$adapter.Status -eq "Up") { break } + Start-Sleep -Seconds 2 + `$timeout -= 2 +} +if (`$timeout -le 0) { exit 1 } +Set-DnsClientServerAddress -InterfaceAlias "nebula1" -ServerAddresses "$DnsServer" + +# Wait for DC to become reachable, then force Netlogon to rediscover +`$timeout = 180 +while (`$timeout -gt 0) { + `$result = Resolve-DnsName "$Domain" -Server "$DnsServer" -ErrorAction SilentlyContinue + if (`$result) { break } + Start-Sleep -Seconds 5 + `$timeout -= 5 +} +if (`$timeout -gt 0) { + Clear-DnsClientCache + nltest /dsgetdc:$NetBIOSDomain /force 2>&1 | Out-Null +} +"@ +Set-Content -Path $StartupScript -Value $startupScriptContent -Force + +Write-Host "Registering NebulaDNS scheduled task..." +$existingTask = Get-ScheduledTask -TaskName "NebulaDNS" -ErrorAction SilentlyContinue +if ($existingTask) { + Unregister-ScheduledTask -TaskName "NebulaDNS" -Confirm:$false +} + +$action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-ExecutionPolicy Bypass -File `"$StartupScript`"" +$trigger = New-ScheduledTaskTrigger -AtStartup +$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -RunLevel Highest +$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries +Register-ScheduledTask -TaskName "NebulaDNS" -Action $action -Trigger $trigger -Principal $principal -Settings $settings -Description "Sets DNS on the Nebula adapter and forces Netlogon DC rediscovery" | Out-Null + +Write-Host "NebulaDNS scheduled task registered." + +# --- Configure Netlogon to wait for slow network links --- +# +# ExpectedDialupDelay tells Netlogon to keep retrying DC discovery for the +# specified number of seconds. This covers the window between Windows boot +# and the Nebula tunnel becoming fully operational. + +$netlogonKey = "HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters" +Set-ItemProperty -Path $netlogonKey -Name "ExpectedDialupDelay" -Value 60 -Type DWord +Write-Host "Netlogon ExpectedDialupDelay set to 60 seconds."