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."