public-ready-init
This commit is contained in:
commit
fc19ef81ec
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# Per-host files (sensitive)
|
||||||
|
host.crt
|
||||||
|
host.key
|
||||||
|
config.yml
|
||||||
|
|
||||||
|
# CA certificate (unique per deployment)
|
||||||
|
ca.crt
|
||||||
14
CHANGELOG.md
Normal file
14
CHANGELOG.md
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
## 1.0.0
|
||||||
|
|
||||||
|
Initial release.
|
||||||
|
|
||||||
|
- `bootstrap.ps1` — Single-command onboarding: installs Nebula, configures DNS, joins Active Directory domain
|
||||||
|
- `install-nebula.ps1` — Installs Nebula as a Windows service with delayed-auto start and NlaSvc dependency
|
||||||
|
- `set-dns.ps1` — Configures DNS on the Nebula tunnel adapter only, preserving physical adapter DNS
|
||||||
|
- `join-domain.ps1` — Joins the domain under the machine's current name with optional computer name validation
|
||||||
|
- Idempotent: all scripts are safe to re-run without side effects
|
||||||
|
- Each script can be run independently for fine-grained control
|
||||||
|
- Pre-staged AD object support: machines renamed before bootstrap auto-match existing computer objects
|
||||||
|
- Bundled with Nebula 1.10.3 and WinTun driver
|
||||||
22
LICENSE
Normal file
22
LICENSE
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 Corellon Digital
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
22
LICENSE-NEBULA
Normal file
22
LICENSE-NEBULA
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2018-2019 Slack Technologies, Inc.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
161
README.md
Normal file
161
README.md
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
# Nebula Domain Bootstrap
|
||||||
|
|
||||||
|
Onboards a Windows machine to the Nebula mesh network and joins it to an Active Directory domain. Designed to be run once by a local Administrator on a fresh Windows install.
|
||||||
|
|
||||||
|
**Bundled Nebula version:** 1.10.3
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
Before running, you need the following **per-host files** prepared by the domain administrator:
|
||||||
|
|
||||||
|
- `config.yml` — Nebula configuration (use forward slashes in all paths)
|
||||||
|
- `host.crt` — Nebula certificate signed by the CA
|
||||||
|
- `host.key` — Nebula private key
|
||||||
|
|
||||||
|
These files are unique to each machine and should be placed in a directory you will run the scripts from.
|
||||||
|
|
||||||
|
## Package Contents
|
||||||
|
|
||||||
|
| File | Purpose |
|
||||||
|
|---|---|
|
||||||
|
| `bootstrap.ps1` | Runs all three steps in sequence |
|
||||||
|
| `install-nebula.ps1` | Installs Nebula as a Windows service |
|
||||||
|
| `set-dns.ps1` | Points the Nebula adapter's DNS at the domain controller |
|
||||||
|
| `join-domain.ps1` | Joins the machine to the domain |
|
||||||
|
| `ca.crt` | Nebula certificate authority |
|
||||||
|
| `nebula.exe` | Nebula binary |
|
||||||
|
| `dist\` | WinTun driver |
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
All scripts require an **Administrator PowerShell session**. If script execution is blocked, run this first:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process
|
||||||
|
```
|
||||||
|
|
||||||
|
### One-shot bootstrap
|
||||||
|
|
||||||
|
From the directory containing your per-host files (`config.yml`, `host.crt`, `host.key`):
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
& "C:\path\to\nebula\bootstrap.ps1" -DnsServer 10.10.10.13 -Domain "arvandor.internal"
|
||||||
|
```
|
||||||
|
|
||||||
|
Optionally pass `-ComputerName` to validate the machine name matches a pre-staged AD object:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
& "C:\path\to\nebula\bootstrap.ps1" -DnsServer 10.10.10.13 -Domain "arvandor.internal" -ComputerName "WS-NAME"
|
||||||
|
```
|
||||||
|
|
||||||
|
This runs all three steps in order, prompts for domain credentials, and offers to restart when finished.
|
||||||
|
|
||||||
|
### Step-by-step
|
||||||
|
|
||||||
|
If you need to run steps individually:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# 1. Install Nebula
|
||||||
|
& "C:\path\to\nebula\install-nebula.ps1"
|
||||||
|
|
||||||
|
# 2. Point DNS at the domain controller
|
||||||
|
& "C:\path\to\nebula\set-dns.ps1" -DnsServer 10.10.10.13
|
||||||
|
|
||||||
|
# 3. Join domain
|
||||||
|
& "C:\path\to\nebula\join-domain.ps1" -Domain "arvandor.internal"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
|
||||||
|
**set-dns.ps1**
|
||||||
|
- `-DnsServer` (required) — IP address of the domain controller
|
||||||
|
- `-InterfaceAlias` (optional) — Target a specific adapter. Defaults to `nebula1`
|
||||||
|
|
||||||
|
**join-domain.ps1**
|
||||||
|
- `-Domain` (required) — FQDN of the Active Directory domain
|
||||||
|
- `-ComputerName` (optional) — Expected NetBIOS name (max 15 characters). If provided and the current machine name doesn't match, the script warns but proceeds with the join under the current name.
|
||||||
|
|
||||||
|
## Deployment Flow
|
||||||
|
|
||||||
|
1. Create a computer object in Active Directory in the appropriate OU
|
||||||
|
2. Sign a Nebula certificate for the machine with the correct group (e.g., `workstations`)
|
||||||
|
3. Install Windows on the target machine
|
||||||
|
4. **If using a pre-staged AD object:** rename the machine to match and reboot before proceeding
|
||||||
|
```powershell
|
||||||
|
Rename-Computer -NewName "WS-NAME" -Restart
|
||||||
|
```
|
||||||
|
5. As a local Administrator, run the bootstrap with the per-host files
|
||||||
|
6. Restart — the machine will authenticate against the domain controller over Nebula at the login screen and pull Group Policy
|
||||||
|
|
||||||
|
## Idempotency
|
||||||
|
|
||||||
|
All scripts are safe to re-run:
|
||||||
|
|
||||||
|
- `install-nebula.ps1` — Skips reinstall if the service is already running with matching files. Tears down and rebuilds if 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.
|
||||||
|
- **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.
|
||||||
|
- **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
|
||||||
|
|
||||||
|
### Scripts won't run
|
||||||
|
PowerShell execution policy is blocking unsigned scripts. Run `Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process` in the same terminal before retrying. This only affects the current session.
|
||||||
|
|
||||||
|
### "This script must be run as a machine Administrator"
|
||||||
|
Right-click PowerShell and select **Run as Administrator**, or use an elevated terminal.
|
||||||
|
|
||||||
|
### Nebula service fails to start
|
||||||
|
Check the Application event log:
|
||||||
|
```powershell
|
||||||
|
Get-WinEvent -FilterHashtable @{LogName='Application'; ProviderName='nebula'} -MaxEvents 10 | Format-List TimeCreated, Message
|
||||||
|
```
|
||||||
|
|
||||||
|
Common causes:
|
||||||
|
- **"found unknown escape character"** — Backslashes in `config.yml` paths. YAML interprets `\` as escape characters inside double quotes. Use forward slashes (`C:/Program Files/Nebula/ca.crt`).
|
||||||
|
- **"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:
|
||||||
|
```powershell
|
||||||
|
sc qc nebula
|
||||||
|
```
|
||||||
|
Expected: `START_TYPE: AUTO_START (DELAYED)` with `DEPENDENCIES: Tcpip, NlaSvc`. Re-run `install-nebula.ps1` to fix.
|
||||||
|
|
||||||
|
### DNS resolution fails
|
||||||
|
Verify DNS is set on the Nebula adapter only:
|
||||||
|
```powershell
|
||||||
|
Get-DnsClientServerAddress -InterfaceAlias "nebula1" -AddressFamily IPv4
|
||||||
|
```
|
||||||
|
If DNS was accidentally set on Wi-Fi/Ethernet, reset it:
|
||||||
|
```powershell
|
||||||
|
Set-DnsClientServerAddress -InterfaceAlias "Wi-Fi" -ResetServerAddresses
|
||||||
|
```
|
||||||
|
|
||||||
|
### Computer name mismatch warning during domain join
|
||||||
|
The script warns if the machine's current name doesn't match the `-ComputerName` parameter. If you have a pre-staged AD object, rename the machine and reboot **before** running bootstrap:
|
||||||
|
```powershell
|
||||||
|
Rename-Computer -NewName "WS-NAME" -Restart
|
||||||
|
```
|
||||||
|
Then re-run bootstrap. The machine will present the correct name to AD and auto-match the pre-staged object.
|
||||||
|
|
||||||
|
### Cannot reach domain controller at login screen
|
||||||
|
The Nebula tunnel must be established before Windows attempts domain authentication. Verify:
|
||||||
|
1. Nebula service is running: `sc query nebula`
|
||||||
|
2. Tunnel is active: `ping 10.10.10.13`
|
||||||
|
3. DNS resolves: `nslookup arvandor.internal`
|
||||||
|
|
||||||
|
If the service is running but handshakes fail, check that the machine's Nebula certificate group has firewall access to the `ad` group.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This project is licensed under the MIT License. See [LICENSE](LICENSE) for details.
|
||||||
|
|
||||||
|
### Third-party licenses
|
||||||
|
|
||||||
|
- **Nebula** (nebula.exe) — MIT License, Copyright (c) 2018-2019 Slack Technologies, Inc. See [LICENSE-NEBULA](LICENSE-NEBULA).
|
||||||
|
- **WinTun** (dist/) — Prebuilt Binaries License, Copyright WireGuard LLC. See [dist/windows/wintun/LICENSE.txt](dist/windows/wintun/LICENSE.txt).
|
||||||
47
bootstrap.ps1
Normal file
47
bootstrap.ps1
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#Requires -RunAsAdministrator
|
||||||
|
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory=$true)]
|
||||||
|
[string]$DnsServer,
|
||||||
|
|
||||||
|
[Parameter(Mandatory=$true)]
|
||||||
|
[string]$Domain,
|
||||||
|
|
||||||
|
[Parameter(Mandatory=$false)]
|
||||||
|
[string]$ComputerName
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
# Verify running as Administrator
|
||||||
|
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
|
||||||
|
$principal = [Security.Principal.WindowsPrincipal]$identity
|
||||||
|
if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
|
||||||
|
Write-Error "This script must be run as a machine Administrator."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Definition
|
||||||
|
|
||||||
|
# --- Step 1: Install Nebula ---
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "=== Step 1/3: Installing Nebula ==="
|
||||||
|
& "$ScriptDir\install-nebula.ps1"
|
||||||
|
if ($LASTEXITCODE -ne 0) { exit 1 }
|
||||||
|
|
||||||
|
# --- Step 2: Set DNS ---
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "=== Step 2/3: Configuring DNS ==="
|
||||||
|
& "$ScriptDir\set-dns.ps1" -DnsServer $DnsServer
|
||||||
|
if ($LASTEXITCODE -ne 0) { exit 1 }
|
||||||
|
|
||||||
|
# --- Step 3: Join Domain ---
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "=== Step 3/3: Joining Domain ==="
|
||||||
|
$joinArgs = @{ Domain = $Domain }
|
||||||
|
if ($ComputerName) { $joinArgs.ComputerName = $ComputerName }
|
||||||
|
& "$ScriptDir\join-domain.ps1" @joinArgs
|
||||||
|
if ($LASTEXITCODE -ne 0) { exit 1 }
|
||||||
84
dist/windows/wintun/LICENSE.txt
vendored
Normal file
84
dist/windows/wintun/LICENSE.txt
vendored
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
Prebuilt Binaries License
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
1. DEFINITIONS. "Software" means the precise contents of the "wintun.dll"
|
||||||
|
files that are included in the .zip file that contains this document as
|
||||||
|
downloaded from wintun.net/builds.
|
||||||
|
|
||||||
|
2. LICENSE GRANT. WireGuard LLC grants to you a non-exclusive and
|
||||||
|
non-transferable right to use Software for lawful purposes under certain
|
||||||
|
obligations and limited rights as set forth in this agreement.
|
||||||
|
|
||||||
|
3. RESTRICTIONS. Software is owned and copyrighted by WireGuard LLC. It is
|
||||||
|
licensed, not sold. Title to Software and all associated intellectual
|
||||||
|
property rights are retained by WireGuard. You must not:
|
||||||
|
a. reverse engineer, decompile, disassemble, extract from, or otherwise
|
||||||
|
modify the Software;
|
||||||
|
b. modify or create derivative work based upon Software in whole or in
|
||||||
|
parts, except insofar as only the API interfaces of the "wintun.h" file
|
||||||
|
distributed alongside the Software (the "Permitted API") are used;
|
||||||
|
c. remove any proprietary notices, labels, or copyrights from the Software;
|
||||||
|
d. resell, redistribute, lease, rent, transfer, sublicense, or otherwise
|
||||||
|
transfer rights of the Software without the prior written consent of
|
||||||
|
WireGuard LLC, except insofar as the Software is distributed alongside
|
||||||
|
other software that uses the Software only via the Permitted API;
|
||||||
|
e. use the name of WireGuard LLC, the WireGuard project, the Wintun
|
||||||
|
project, or the names of its contributors to endorse or promote products
|
||||||
|
derived from the Software without specific prior written consent.
|
||||||
|
|
||||||
|
4. LIMITED WARRANTY. THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTY OF
|
||||||
|
ANY KIND. WIREGUARD LLC HEREBY EXCLUDES AND DISCLAIMS ALL IMPLIED OR
|
||||||
|
STATUTORY WARRANTIES, INCLUDING ANY WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE, QUALITY, NON-INFRINGEMENT, TITLE, RESULTS,
|
||||||
|
EFFORTS, OR QUIET ENJOYMENT. THERE IS NO WARRANTY THAT THE PRODUCT WILL BE
|
||||||
|
ERROR-FREE OR WILL FUNCTION WITHOUT INTERRUPTION. YOU ASSUME THE ENTIRE
|
||||||
|
RISK FOR THE RESULTS OBTAINED USING THE PRODUCT. TO THE EXTENT THAT
|
||||||
|
WIREGUARD LLC MAY NOT DISCLAIM ANY WARRANTY AS A MATTER OF APPLICABLE LAW,
|
||||||
|
THE SCOPE AND DURATION OF SUCH WARRANTY WILL BE THE MINIMUM PERMITTED UNDER
|
||||||
|
SUCH LAW. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
|
||||||
|
WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE OR NON-INFRINGEMENT ARE DISCLAIMED, EXCEPT TO THE
|
||||||
|
EXTENT THAT THESE DISCLAIMERS ARE HELD TO BE LEGALLY INVALID.
|
||||||
|
|
||||||
|
5. LIMITATION OF LIABILITY. To the extent not prohibited by law, in no event
|
||||||
|
WireGuard LLC or any third-party-developer will be liable for any lost
|
||||||
|
revenue, profit or data or for special, indirect, consequential, incidental
|
||||||
|
or punitive damages, however caused regardless of the theory of liability,
|
||||||
|
arising out of or related to the use of or inability to use Software, even
|
||||||
|
if WireGuard LLC has been advised of the possibility of such damages.
|
||||||
|
Solely you are responsible for determining the appropriateness of using
|
||||||
|
Software and accept full responsibility for all risks associated with its
|
||||||
|
exercise of rights under this agreement, including but not limited to the
|
||||||
|
risks and costs of program errors, compliance with applicable laws, damage
|
||||||
|
to or loss of data, programs or equipment, and unavailability or
|
||||||
|
interruption of operations. The foregoing limitations will apply even if
|
||||||
|
the above stated warranty fails of its essential purpose. You acknowledge,
|
||||||
|
that it is in the nature of software that software is complex and not
|
||||||
|
completely free of errors. In no event shall WireGuard LLC or any
|
||||||
|
third-party-developer be liable to you under any theory for any damages
|
||||||
|
suffered by you or any user of Software or for any special, incidental,
|
||||||
|
indirect, consequential or similar damages (including without limitation
|
||||||
|
damages for loss of business profits, business interruption, loss of
|
||||||
|
business information or any other pecuniary loss) arising out of the use or
|
||||||
|
inability to use Software, even if WireGuard LLC has been advised of the
|
||||||
|
possibility of such damages and regardless of the legal or quitable theory
|
||||||
|
(contract, tort, or otherwise) upon which the claim is based.
|
||||||
|
|
||||||
|
6. TERMINATION. This agreement is affected until terminated. You may
|
||||||
|
terminate this agreement at any time. This agreement will terminate
|
||||||
|
immediately without notice from WireGuard LLC if you fail to comply with
|
||||||
|
the terms and conditions of this agreement. Upon termination, you must
|
||||||
|
delete Software and all copies of Software and cease all forms of
|
||||||
|
distribution of Software.
|
||||||
|
|
||||||
|
7. SEVERABILITY. If any provision of this agreement is held to be
|
||||||
|
unenforceable, this agreement will remain in effect with the provision
|
||||||
|
omitted, unless omission would frustrate the intent of the parties, in
|
||||||
|
which case this agreement will immediately terminate.
|
||||||
|
|
||||||
|
8. RESERVATION OF RIGHTS. All rights not expressly granted in this agreement
|
||||||
|
are reserved by WireGuard LLC. For example, WireGuard LLC reserves the
|
||||||
|
right at any time to cease development of Software, to alter distribution
|
||||||
|
details, features, specifications, capabilities, functions, licensing
|
||||||
|
terms, release dates, APIs, ABIs, general availability, or other
|
||||||
|
characteristics of the Software.
|
||||||
339
dist/windows/wintun/README.md
vendored
Normal file
339
dist/windows/wintun/README.md
vendored
Normal file
@ -0,0 +1,339 @@
|
|||||||
|
# [Wintun Network Adapter](https://www.wintun.net/)
|
||||||
|
### TUN Device Driver for Windows
|
||||||
|
|
||||||
|
This is a layer 3 TUN driver for Windows 7, 8, 8.1, and 10. Originally created for [WireGuard](https://www.wireguard.com/), it is intended to be useful to a wide variety of projects that require layer 3 tunneling devices with implementations primarily in userspace.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Wintun is deployed as a platform-specific `wintun.dll` file. Install the `wintun.dll` file side-by-side with your application. Download the dll from [wintun.net](https://www.wintun.net/), alongside the header file for your application described below.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Include the [`wintun.h` file](https://git.zx2c4.com/wintun/tree/api/wintun.h) in your project simply by copying it there and dynamically load the `wintun.dll` using [`LoadLibraryEx()`](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexa) and [`GetProcAddress()`](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getprocaddress) to resolve each function, using the typedefs provided in the header file. The [`InitializeWintun` function in the example.c code](https://git.zx2c4.com/wintun/tree/example/example.c) provides this in a function that you can simply copy and paste.
|
||||||
|
|
||||||
|
With the library setup, Wintun can then be used by first creating an adapter, configuring it, and then setting its status to "up". Adapters have names (e.g. "OfficeNet") and types (e.g. "Wintun").
|
||||||
|
|
||||||
|
```C
|
||||||
|
WINTUN_ADAPTER_HANDLE Adapter1 = WintunCreateAdapter(L"OfficeNet", L"Wintun", &SomeFixedGUID1);
|
||||||
|
WINTUN_ADAPTER_HANDLE Adapter2 = WintunCreateAdapter(L"HomeNet", L"Wintun", &SomeFixedGUID2);
|
||||||
|
WINTUN_ADAPTER_HANDLE Adapter3 = WintunCreateAdapter(L"Data Center", L"Wintun", &SomeFixedGUID3);
|
||||||
|
```
|
||||||
|
|
||||||
|
After creating an adapter, we can use it by starting a session:
|
||||||
|
|
||||||
|
```C
|
||||||
|
WINTUN_SESSION_HANDLE Session = WintunStartSession(Adapter2, 0x400000);
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, the `WintunAllocateSendPacket` and `WintunSendPacket` functions can be used for sending packets ([used by `SendPackets` in the example.c code](https://git.zx2c4.com/wintun/tree/example/example.c)):
|
||||||
|
|
||||||
|
```C
|
||||||
|
BYTE *OutgoingPacket = WintunAllocateSendPacket(Session, PacketDataSize);
|
||||||
|
if (OutgoingPacket)
|
||||||
|
{
|
||||||
|
memcpy(OutgoingPacket, PacketData, PacketDataSize);
|
||||||
|
WintunSendPacket(Session, OutgoingPacket);
|
||||||
|
}
|
||||||
|
else if (GetLastError() != ERROR_BUFFER_OVERFLOW) // Silently drop packets if the ring is full
|
||||||
|
Log(L"Packet write failed");
|
||||||
|
```
|
||||||
|
|
||||||
|
And the `WintunReceivePacket` and `WintunReleaseReceivePacket` functions can be used for receiving packets ([used by `ReceivePackets` in the example.c code](https://git.zx2c4.com/wintun/tree/example/example.c)):
|
||||||
|
|
||||||
|
```C
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
DWORD IncomingPacketSize;
|
||||||
|
BYTE *IncomingPacket = WintunReceivePacket(Session, &IncomingPacketSize);
|
||||||
|
if (IncomingPacket)
|
||||||
|
{
|
||||||
|
DoSomethingWithPacket(IncomingPacket, IncomingPacketSize);
|
||||||
|
WintunReleaseReceivePacket(Session, IncomingPacket);
|
||||||
|
}
|
||||||
|
else if (GetLastError() == ERROR_NO_MORE_ITEMS)
|
||||||
|
WaitForSingleObject(WintunGetReadWaitEvent(Session), INFINITE);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log(L"Packet read failed");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Some high performance use cases may want to spin on `WintunReceivePackets` for a number of cycles before falling back to waiting on the read-wait event.
|
||||||
|
|
||||||
|
You are **highly encouraged** to read the [**example.c short example**](https://git.zx2c4.com/wintun/tree/example/example.c) to see how to put together a simple userspace network tunnel.
|
||||||
|
|
||||||
|
The various functions and definitions are [documented in the reference below](#Reference).
|
||||||
|
|
||||||
|
## Reference
|
||||||
|
|
||||||
|
### Macro Definitions
|
||||||
|
|
||||||
|
#### WINTUN\_MAX\_POOL
|
||||||
|
|
||||||
|
`#define WINTUN_MAX_POOL 256`
|
||||||
|
|
||||||
|
Maximum pool name length including zero terminator
|
||||||
|
|
||||||
|
#### WINTUN\_MIN\_RING\_CAPACITY
|
||||||
|
|
||||||
|
`#define WINTUN_MIN_RING_CAPACITY 0x20000 /* 128kiB */`
|
||||||
|
|
||||||
|
Minimum ring capacity.
|
||||||
|
|
||||||
|
#### WINTUN\_MAX\_RING\_CAPACITY
|
||||||
|
|
||||||
|
`#define WINTUN_MAX_RING_CAPACITY 0x4000000 /* 64MiB */`
|
||||||
|
|
||||||
|
Maximum ring capacity.
|
||||||
|
|
||||||
|
#### WINTUN\_MAX\_IP\_PACKET\_SIZE
|
||||||
|
|
||||||
|
`#define WINTUN_MAX_IP_PACKET_SIZE 0xFFFF`
|
||||||
|
|
||||||
|
Maximum IP packet size
|
||||||
|
|
||||||
|
### Typedefs
|
||||||
|
|
||||||
|
#### WINTUN\_ADAPTER\_HANDLE
|
||||||
|
|
||||||
|
`typedef void* WINTUN_ADAPTER_HANDLE`
|
||||||
|
|
||||||
|
A handle representing Wintun adapter
|
||||||
|
|
||||||
|
#### WINTUN\_ENUM\_CALLBACK
|
||||||
|
|
||||||
|
`typedef BOOL(* WINTUN_ENUM_CALLBACK) (WINTUN_ADAPTER_HANDLE Adapter, LPARAM Param)`
|
||||||
|
|
||||||
|
Called by WintunEnumAdapters for each adapter in the pool.
|
||||||
|
|
||||||
|
**Parameters**
|
||||||
|
|
||||||
|
- *Adapter*: Adapter handle, which will be freed when this function returns.
|
||||||
|
- *Param*: An application-defined value passed to the WintunEnumAdapters.
|
||||||
|
|
||||||
|
**Returns**
|
||||||
|
|
||||||
|
Non-zero to continue iterating adapters; zero to stop.
|
||||||
|
|
||||||
|
#### WINTUN\_LOGGER\_CALLBACK
|
||||||
|
|
||||||
|
`typedef void(* WINTUN_LOGGER_CALLBACK) (WINTUN_LOGGER_LEVEL Level, DWORD64 Timestamp, const WCHAR *Message)`
|
||||||
|
|
||||||
|
Called by internal logger to report diagnostic messages
|
||||||
|
|
||||||
|
**Parameters**
|
||||||
|
|
||||||
|
- *Level*: Message level.
|
||||||
|
- *Timestamp*: Message timestamp in in 100ns intervals since 1601-01-01 UTC.
|
||||||
|
- *Message*: Message text.
|
||||||
|
|
||||||
|
#### WINTUN\_SESSION\_HANDLE
|
||||||
|
|
||||||
|
`typedef void* WINTUN_SESSION_HANDLE`
|
||||||
|
|
||||||
|
A handle representing Wintun session
|
||||||
|
|
||||||
|
### Enumeration Types
|
||||||
|
|
||||||
|
#### WINTUN\_LOGGER\_LEVEL
|
||||||
|
|
||||||
|
`enum WINTUN_LOGGER_LEVEL`
|
||||||
|
|
||||||
|
Determines the level of logging, passed to WINTUN\_LOGGER\_CALLBACK.
|
||||||
|
|
||||||
|
- *WINTUN\_LOG\_INFO*: Informational
|
||||||
|
- *WINTUN\_LOG\_WARN*: Warning
|
||||||
|
- *WINTUN\_LOG\_ERR*: Error
|
||||||
|
|
||||||
|
Enumerator
|
||||||
|
|
||||||
|
### Functions
|
||||||
|
|
||||||
|
#### WintunCreateAdapter()
|
||||||
|
|
||||||
|
`WINTUN_ADAPTER_HANDLE WintunCreateAdapter (const WCHAR * Name, const WCHAR * TunnelType, const GUID * RequestedGUID)`
|
||||||
|
|
||||||
|
Creates a new Wintun adapter.
|
||||||
|
|
||||||
|
**Parameters**
|
||||||
|
|
||||||
|
- *Name*: The requested name of the adapter. Zero-terminated string of up to MAX\_ADAPTER\_NAME-1 characters.
|
||||||
|
- *Name*: Name of the adapter tunnel type. Zero-terminated string of up to MAX\_ADAPTER\_NAME-1 characters.
|
||||||
|
- *RequestedGUID*: The GUID of the created network adapter, which then influences NLA generation deterministically. If it is set to NULL, the GUID is chosen by the system at random, and hence a new NLA entry is created for each new adapter. It is called "requested" GUID because the API it uses is completely undocumented, and so there could be minor interesting complications with its usage.
|
||||||
|
|
||||||
|
**Returns**
|
||||||
|
|
||||||
|
If the function succeeds, the return value is the adapter handle. Must be released with WintunCloseAdapter. If the function fails, the return value is NULL. To get extended error information, call GetLastError.
|
||||||
|
|
||||||
|
#### WintunOpenAdapter()
|
||||||
|
|
||||||
|
`WINTUN_ADAPTER_HANDLE WintunOpenAdapter (const WCHAR * Name)`
|
||||||
|
|
||||||
|
Opens an existing Wintun adapter.
|
||||||
|
|
||||||
|
**Parameters**
|
||||||
|
|
||||||
|
- *Name*: The requested name of the adapter. Zero-terminated string of up to MAX\_ADAPTER\_NAME-1 characters.
|
||||||
|
|
||||||
|
**Returns**
|
||||||
|
|
||||||
|
If the function succeeds, the return value is adapter handle. Must be released with WintunCloseAdapter. If the function fails, the return value is NULL. To get extended error information, call GetLastError.
|
||||||
|
|
||||||
|
#### WintunCloseAdapter()
|
||||||
|
|
||||||
|
`void WintunCloseAdapter (WINTUN_ADAPTER_HANDLE Adapter)`
|
||||||
|
|
||||||
|
Releases Wintun adapter resources and, if adapter was created with WintunCreateAdapter, removes adapter.
|
||||||
|
|
||||||
|
**Parameters**
|
||||||
|
|
||||||
|
- *Adapter*: Adapter handle obtained with WintunCreateAdapter or WintunOpenAdapter.
|
||||||
|
|
||||||
|
#### WintunDeleteDriver()
|
||||||
|
|
||||||
|
`BOOL WintunDeleteDriver ()`
|
||||||
|
|
||||||
|
Deletes the Wintun driver if there are no more adapters in use.
|
||||||
|
|
||||||
|
**Returns**
|
||||||
|
|
||||||
|
If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To get extended error information, call GetLastError.
|
||||||
|
|
||||||
|
#### WintunGetAdapterLuid()
|
||||||
|
|
||||||
|
`void WintunGetAdapterLuid (WINTUN_ADAPTER_HANDLE Adapter, NET_LUID * Luid)`
|
||||||
|
|
||||||
|
Returns the LUID of the adapter.
|
||||||
|
|
||||||
|
**Parameters**
|
||||||
|
|
||||||
|
- *Adapter*: Adapter handle obtained with WintunOpenAdapter or WintunCreateAdapter
|
||||||
|
- *Luid*: Pointer to LUID to receive adapter LUID.
|
||||||
|
|
||||||
|
#### WintunGetRunningDriverVersion()
|
||||||
|
|
||||||
|
`DWORD WintunGetRunningDriverVersion (void )`
|
||||||
|
|
||||||
|
Determines the version of the Wintun driver currently loaded.
|
||||||
|
|
||||||
|
**Returns**
|
||||||
|
|
||||||
|
If the function succeeds, the return value is the version number. If the function fails, the return value is zero. To get extended error information, call GetLastError. Possible errors include the following: ERROR\_FILE\_NOT\_FOUND Wintun not loaded
|
||||||
|
|
||||||
|
#### WintunSetLogger()
|
||||||
|
|
||||||
|
`void WintunSetLogger (WINTUN_LOGGER_CALLBACK NewLogger)`
|
||||||
|
|
||||||
|
Sets logger callback function.
|
||||||
|
|
||||||
|
**Parameters**
|
||||||
|
|
||||||
|
- *NewLogger*: Pointer to callback function to use as a new global logger. NewLogger may be called from various threads concurrently. Should the logging require serialization, you must handle serialization in NewLogger. Set to NULL to disable.
|
||||||
|
|
||||||
|
#### WintunStartSession()
|
||||||
|
|
||||||
|
`WINTUN_SESSION_HANDLE WintunStartSession (WINTUN_ADAPTER_HANDLE Adapter, DWORD Capacity)`
|
||||||
|
|
||||||
|
Starts Wintun session.
|
||||||
|
|
||||||
|
**Parameters**
|
||||||
|
|
||||||
|
- *Adapter*: Adapter handle obtained with WintunOpenAdapter or WintunCreateAdapter
|
||||||
|
- *Capacity*: Rings capacity. Must be between WINTUN\_MIN\_RING\_CAPACITY and WINTUN\_MAX\_RING\_CAPACITY (incl.) Must be a power of two.
|
||||||
|
|
||||||
|
**Returns**
|
||||||
|
|
||||||
|
Wintun session handle. Must be released with WintunEndSession. If the function fails, the return value is NULL. To get extended error information, call GetLastError.
|
||||||
|
|
||||||
|
#### WintunEndSession()
|
||||||
|
|
||||||
|
`void WintunEndSession (WINTUN_SESSION_HANDLE Session)`
|
||||||
|
|
||||||
|
Ends Wintun session.
|
||||||
|
|
||||||
|
**Parameters**
|
||||||
|
|
||||||
|
- *Session*: Wintun session handle obtained with WintunStartSession
|
||||||
|
|
||||||
|
#### WintunGetReadWaitEvent()
|
||||||
|
|
||||||
|
`HANDLE WintunGetReadWaitEvent (WINTUN_SESSION_HANDLE Session)`
|
||||||
|
|
||||||
|
Gets Wintun session's read-wait event handle.
|
||||||
|
|
||||||
|
**Parameters**
|
||||||
|
|
||||||
|
- *Session*: Wintun session handle obtained with WintunStartSession
|
||||||
|
|
||||||
|
**Returns**
|
||||||
|
|
||||||
|
Pointer to receive event handle to wait for available data when reading. Should WintunReceivePackets return ERROR\_NO\_MORE\_ITEMS (after spinning on it for a while under heavy load), wait for this event to become signaled before retrying WintunReceivePackets. Do not call CloseHandle on this event - it is managed by the session.
|
||||||
|
|
||||||
|
#### WintunReceivePacket()
|
||||||
|
|
||||||
|
`BYTE* WintunReceivePacket (WINTUN_SESSION_HANDLE Session, DWORD * PacketSize)`
|
||||||
|
|
||||||
|
Retrieves one or packet. After the packet content is consumed, call WintunReleaseReceivePacket with Packet returned from this function to release internal buffer. This function is thread-safe.
|
||||||
|
|
||||||
|
**Parameters**
|
||||||
|
|
||||||
|
- *Session*: Wintun session handle obtained with WintunStartSession
|
||||||
|
- *PacketSize*: Pointer to receive packet size.
|
||||||
|
|
||||||
|
**Returns**
|
||||||
|
|
||||||
|
Pointer to layer 3 IPv4 or IPv6 packet. Client may modify its content at will. If the function fails, the return value is NULL. To get extended error information, call GetLastError. Possible errors include the following: ERROR\_HANDLE\_EOF Wintun adapter is terminating; ERROR\_NO\_MORE\_ITEMS Wintun buffer is exhausted; ERROR\_INVALID\_DATA Wintun buffer is corrupt
|
||||||
|
|
||||||
|
#### WintunReleaseReceivePacket()
|
||||||
|
|
||||||
|
`void WintunReleaseReceivePacket (WINTUN_SESSION_HANDLE Session, const BYTE * Packet)`
|
||||||
|
|
||||||
|
Releases internal buffer after the received packet has been processed by the client. This function is thread-safe.
|
||||||
|
|
||||||
|
**Parameters**
|
||||||
|
|
||||||
|
- *Session*: Wintun session handle obtained with WintunStartSession
|
||||||
|
- *Packet*: Packet obtained with WintunReceivePacket
|
||||||
|
|
||||||
|
#### WintunAllocateSendPacket()
|
||||||
|
|
||||||
|
`BYTE* WintunAllocateSendPacket (WINTUN_SESSION_HANDLE Session, DWORD PacketSize)`
|
||||||
|
|
||||||
|
Allocates memory for a packet to send. After the memory is filled with packet data, call WintunSendPacket to send and release internal buffer. WintunAllocateSendPacket is thread-safe and the WintunAllocateSendPacket order of calls define the packet sending order.
|
||||||
|
|
||||||
|
**Parameters**
|
||||||
|
|
||||||
|
- *Session*: Wintun session handle obtained with WintunStartSession
|
||||||
|
- *PacketSize*: Exact packet size. Must be less or equal to WINTUN\_MAX\_IP\_PACKET\_SIZE.
|
||||||
|
|
||||||
|
**Returns**
|
||||||
|
|
||||||
|
Returns pointer to memory where to prepare layer 3 IPv4 or IPv6 packet for sending. If the function fails, the return value is NULL. To get extended error information, call GetLastError. Possible errors include the following: ERROR\_HANDLE\_EOF Wintun adapter is terminating; ERROR\_BUFFER\_OVERFLOW Wintun buffer is full;
|
||||||
|
|
||||||
|
#### WintunSendPacket()
|
||||||
|
|
||||||
|
`void WintunSendPacket (WINTUN_SESSION_HANDLE Session, const BYTE * Packet)`
|
||||||
|
|
||||||
|
Sends the packet and releases internal buffer. WintunSendPacket is thread-safe, but the WintunAllocateSendPacket order of calls define the packet sending order. This means the packet is not guaranteed to be sent in the WintunSendPacket yet.
|
||||||
|
|
||||||
|
**Parameters**
|
||||||
|
|
||||||
|
- *Session*: Wintun session handle obtained with WintunStartSession
|
||||||
|
- *Packet*: Packet obtained with WintunAllocateSendPacket
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
**Do not distribute drivers or files named "Wintun", as they will most certainly clash with official deployments. Instead distribute [`wintun.dll` as downloaded from wintun.net](https://www.wintun.net).**
|
||||||
|
|
||||||
|
General requirements:
|
||||||
|
|
||||||
|
- [Visual Studio 2019](https://visualstudio.microsoft.com/downloads/) with Windows SDK
|
||||||
|
- [Windows Driver Kit](https://docs.microsoft.com/en-us/windows-hardware/drivers/download-the-wdk)
|
||||||
|
|
||||||
|
`wintun.sln` may be opened in Visual Studio for development and building. Be sure to run `bcdedit /set testsigning on` and then reboot before to enable unsigned driver loading. The default run sequence (F5) in Visual Studio will build the example project and its dependencies.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
The entire contents of [the repository](https://git.zx2c4.com/wintun/), including all documentation and example code, is "Copyright © 2018-2021 WireGuard LLC. All Rights Reserved." Source code is licensed under the [GPLv2](COPYING). Prebuilt binaries from [wintun.net](https://www.wintun.net/) are released under a more permissive license suitable for more forms of software contained inside of the .zip files distributed there.
|
||||||
BIN
dist/windows/wintun/bin/amd64/wintun.dll
vendored
Normal file
BIN
dist/windows/wintun/bin/amd64/wintun.dll
vendored
Normal file
Binary file not shown.
BIN
dist/windows/wintun/bin/arm/wintun.dll
vendored
Normal file
BIN
dist/windows/wintun/bin/arm/wintun.dll
vendored
Normal file
Binary file not shown.
BIN
dist/windows/wintun/bin/arm64/wintun.dll
vendored
Normal file
BIN
dist/windows/wintun/bin/arm64/wintun.dll
vendored
Normal file
Binary file not shown.
BIN
dist/windows/wintun/bin/x86/wintun.dll
vendored
Normal file
BIN
dist/windows/wintun/bin/x86/wintun.dll
vendored
Normal file
Binary file not shown.
270
dist/windows/wintun/include/wintun.h
vendored
Normal file
270
dist/windows/wintun/include/wintun.h
vendored
Normal file
@ -0,0 +1,270 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0 OR MIT
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#include <ipexport.h>
|
||||||
|
#include <ifdef.h>
|
||||||
|
#include <ws2ipdef.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ALIGNED
|
||||||
|
# if defined(_MSC_VER)
|
||||||
|
# define ALIGNED(n) __declspec(align(n))
|
||||||
|
# elif defined(__GNUC__)
|
||||||
|
# define ALIGNED(n) __attribute__((aligned(n)))
|
||||||
|
# else
|
||||||
|
# error "Unable to define ALIGNED"
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* MinGW is missing this one, unfortunately. */
|
||||||
|
#ifndef _Post_maybenull_
|
||||||
|
# define _Post_maybenull_
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable : 4324) /* structure was padded due to alignment specifier */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A handle representing Wintun adapter
|
||||||
|
*/
|
||||||
|
typedef struct _WINTUN_ADAPTER *WINTUN_ADAPTER_HANDLE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Wintun adapter.
|
||||||
|
*
|
||||||
|
* @param Name The requested name of the adapter. Zero-terminated string of up to MAX_ADAPTER_NAME-1
|
||||||
|
* characters.
|
||||||
|
*
|
||||||
|
* @param TunnelType Name of the adapter tunnel type. Zero-terminated string of up to MAX_ADAPTER_NAME-1
|
||||||
|
* characters.
|
||||||
|
*
|
||||||
|
* @param RequestedGUID The GUID of the created network adapter, which then influences NLA generation deterministically.
|
||||||
|
* If it is set to NULL, the GUID is chosen by the system at random, and hence a new NLA entry is
|
||||||
|
* created for each new adapter. It is called "requested" GUID because the API it uses is
|
||||||
|
* completely undocumented, and so there could be minor interesting complications with its usage.
|
||||||
|
*
|
||||||
|
* @return If the function succeeds, the return value is the adapter handle. Must be released with
|
||||||
|
* WintunCloseAdapter. If the function fails, the return value is NULL. To get extended error information, call
|
||||||
|
* GetLastError.
|
||||||
|
*/
|
||||||
|
typedef _Must_inspect_result_
|
||||||
|
_Return_type_success_(return != NULL)
|
||||||
|
_Post_maybenull_
|
||||||
|
WINTUN_ADAPTER_HANDLE(WINAPI WINTUN_CREATE_ADAPTER_FUNC)
|
||||||
|
(_In_z_ LPCWSTR Name, _In_z_ LPCWSTR TunnelType, _In_opt_ const GUID *RequestedGUID);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens an existing Wintun adapter.
|
||||||
|
*
|
||||||
|
* @param Name The requested name of the adapter. Zero-terminated string of up to MAX_ADAPTER_NAME-1
|
||||||
|
* characters.
|
||||||
|
*
|
||||||
|
* @return If the function succeeds, the return value is the adapter handle. Must be released with
|
||||||
|
* WintunCloseAdapter. If the function fails, the return value is NULL. To get extended error information, call
|
||||||
|
* GetLastError.
|
||||||
|
*/
|
||||||
|
typedef _Must_inspect_result_
|
||||||
|
_Return_type_success_(return != NULL)
|
||||||
|
_Post_maybenull_
|
||||||
|
WINTUN_ADAPTER_HANDLE(WINAPI WINTUN_OPEN_ADAPTER_FUNC)(_In_z_ LPCWSTR Name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Releases Wintun adapter resources and, if adapter was created with WintunCreateAdapter, removes adapter.
|
||||||
|
*
|
||||||
|
* @param Adapter Adapter handle obtained with WintunCreateAdapter or WintunOpenAdapter.
|
||||||
|
*/
|
||||||
|
typedef VOID(WINAPI WINTUN_CLOSE_ADAPTER_FUNC)(_In_opt_ WINTUN_ADAPTER_HANDLE Adapter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the Wintun driver if there are no more adapters in use.
|
||||||
|
*
|
||||||
|
* @return If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To
|
||||||
|
* get extended error information, call GetLastError.
|
||||||
|
*/
|
||||||
|
typedef _Return_type_success_(return != FALSE)
|
||||||
|
BOOL(WINAPI WINTUN_DELETE_DRIVER_FUNC)(VOID);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the LUID of the adapter.
|
||||||
|
*
|
||||||
|
* @param Adapter Adapter handle obtained with WintunCreateAdapter or WintunOpenAdapter
|
||||||
|
*
|
||||||
|
* @param Luid Pointer to LUID to receive adapter LUID.
|
||||||
|
*/
|
||||||
|
typedef VOID(WINAPI WINTUN_GET_ADAPTER_LUID_FUNC)(_In_ WINTUN_ADAPTER_HANDLE Adapter, _Out_ NET_LUID *Luid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines the version of the Wintun driver currently loaded.
|
||||||
|
*
|
||||||
|
* @return If the function succeeds, the return value is the version number. If the function fails, the return value is
|
||||||
|
* zero. To get extended error information, call GetLastError. Possible errors include the following:
|
||||||
|
* ERROR_FILE_NOT_FOUND Wintun not loaded
|
||||||
|
*/
|
||||||
|
typedef _Return_type_success_(return != 0)
|
||||||
|
DWORD(WINAPI WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC)(VOID);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines the level of logging, passed to WINTUN_LOGGER_CALLBACK.
|
||||||
|
*/
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
WINTUN_LOG_INFO, /**< Informational */
|
||||||
|
WINTUN_LOG_WARN, /**< Warning */
|
||||||
|
WINTUN_LOG_ERR /**< Error */
|
||||||
|
} WINTUN_LOGGER_LEVEL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by internal logger to report diagnostic messages
|
||||||
|
*
|
||||||
|
* @param Level Message level.
|
||||||
|
*
|
||||||
|
* @param Timestamp Message timestamp in in 100ns intervals since 1601-01-01 UTC.
|
||||||
|
*
|
||||||
|
* @param Message Message text.
|
||||||
|
*/
|
||||||
|
typedef VOID(CALLBACK *WINTUN_LOGGER_CALLBACK)(
|
||||||
|
_In_ WINTUN_LOGGER_LEVEL Level,
|
||||||
|
_In_ DWORD64 Timestamp,
|
||||||
|
_In_z_ LPCWSTR Message);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets logger callback function.
|
||||||
|
*
|
||||||
|
* @param NewLogger Pointer to callback function to use as a new global logger. NewLogger may be called from various
|
||||||
|
* threads concurrently. Should the logging require serialization, you must handle serialization in
|
||||||
|
* NewLogger. Set to NULL to disable.
|
||||||
|
*/
|
||||||
|
typedef VOID(WINAPI WINTUN_SET_LOGGER_FUNC)(_In_ WINTUN_LOGGER_CALLBACK NewLogger);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minimum ring capacity.
|
||||||
|
*/
|
||||||
|
#define WINTUN_MIN_RING_CAPACITY 0x20000 /* 128kiB */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum ring capacity.
|
||||||
|
*/
|
||||||
|
#define WINTUN_MAX_RING_CAPACITY 0x4000000 /* 64MiB */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A handle representing Wintun session
|
||||||
|
*/
|
||||||
|
typedef struct _TUN_SESSION *WINTUN_SESSION_HANDLE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts Wintun session.
|
||||||
|
*
|
||||||
|
* @param Adapter Adapter handle obtained with WintunOpenAdapter or WintunCreateAdapter
|
||||||
|
*
|
||||||
|
* @param Capacity Rings capacity. Must be between WINTUN_MIN_RING_CAPACITY and WINTUN_MAX_RING_CAPACITY (incl.)
|
||||||
|
* Must be a power of two.
|
||||||
|
*
|
||||||
|
* @return Wintun session handle. Must be released with WintunEndSession. If the function fails, the return value is
|
||||||
|
* NULL. To get extended error information, call GetLastError.
|
||||||
|
*/
|
||||||
|
typedef _Must_inspect_result_
|
||||||
|
_Return_type_success_(return != NULL)
|
||||||
|
_Post_maybenull_
|
||||||
|
WINTUN_SESSION_HANDLE(WINAPI WINTUN_START_SESSION_FUNC)(_In_ WINTUN_ADAPTER_HANDLE Adapter, _In_ DWORD Capacity);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ends Wintun session.
|
||||||
|
*
|
||||||
|
* @param Session Wintun session handle obtained with WintunStartSession
|
||||||
|
*/
|
||||||
|
typedef VOID(WINAPI WINTUN_END_SESSION_FUNC)(_In_ WINTUN_SESSION_HANDLE Session);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets Wintun session's read-wait event handle.
|
||||||
|
*
|
||||||
|
* @param Session Wintun session handle obtained with WintunStartSession
|
||||||
|
*
|
||||||
|
* @return Pointer to receive event handle to wait for available data when reading. Should
|
||||||
|
* WintunReceivePackets return ERROR_NO_MORE_ITEMS (after spinning on it for a while under heavy
|
||||||
|
* load), wait for this event to become signaled before retrying WintunReceivePackets. Do not call
|
||||||
|
* CloseHandle on this event - it is managed by the session.
|
||||||
|
*/
|
||||||
|
typedef HANDLE(WINAPI WINTUN_GET_READ_WAIT_EVENT_FUNC)(_In_ WINTUN_SESSION_HANDLE Session);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum IP packet size
|
||||||
|
*/
|
||||||
|
#define WINTUN_MAX_IP_PACKET_SIZE 0xFFFF
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves one or packet. After the packet content is consumed, call WintunReleaseReceivePacket with Packet returned
|
||||||
|
* from this function to release internal buffer. This function is thread-safe.
|
||||||
|
*
|
||||||
|
* @param Session Wintun session handle obtained with WintunStartSession
|
||||||
|
*
|
||||||
|
* @param PacketSize Pointer to receive packet size.
|
||||||
|
*
|
||||||
|
* @return Pointer to layer 3 IPv4 or IPv6 packet. Client may modify its content at will. If the function fails, the
|
||||||
|
* return value is NULL. To get extended error information, call GetLastError. Possible errors include the
|
||||||
|
* following:
|
||||||
|
* ERROR_HANDLE_EOF Wintun adapter is terminating;
|
||||||
|
* ERROR_NO_MORE_ITEMS Wintun buffer is exhausted;
|
||||||
|
* ERROR_INVALID_DATA Wintun buffer is corrupt
|
||||||
|
*/
|
||||||
|
typedef _Must_inspect_result_
|
||||||
|
_Return_type_success_(return != NULL)
|
||||||
|
_Post_maybenull_
|
||||||
|
_Post_writable_byte_size_(*PacketSize)
|
||||||
|
BYTE *(WINAPI WINTUN_RECEIVE_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _Out_ DWORD *PacketSize);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Releases internal buffer after the received packet has been processed by the client. This function is thread-safe.
|
||||||
|
*
|
||||||
|
* @param Session Wintun session handle obtained with WintunStartSession
|
||||||
|
*
|
||||||
|
* @param Packet Packet obtained with WintunReceivePacket
|
||||||
|
*/
|
||||||
|
typedef VOID(
|
||||||
|
WINAPI WINTUN_RELEASE_RECEIVE_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _In_ const BYTE *Packet);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocates memory for a packet to send. After the memory is filled with packet data, call WintunSendPacket to send
|
||||||
|
* and release internal buffer. WintunAllocateSendPacket is thread-safe and the WintunAllocateSendPacket order of
|
||||||
|
* calls define the packet sending order.
|
||||||
|
*
|
||||||
|
* @param Session Wintun session handle obtained with WintunStartSession
|
||||||
|
*
|
||||||
|
* @param PacketSize Exact packet size. Must be less or equal to WINTUN_MAX_IP_PACKET_SIZE.
|
||||||
|
*
|
||||||
|
* @return Returns pointer to memory where to prepare layer 3 IPv4 or IPv6 packet for sending. If the function fails,
|
||||||
|
* the return value is NULL. To get extended error information, call GetLastError. Possible errors include the
|
||||||
|
* following:
|
||||||
|
* ERROR_HANDLE_EOF Wintun adapter is terminating;
|
||||||
|
* ERROR_BUFFER_OVERFLOW Wintun buffer is full;
|
||||||
|
*/
|
||||||
|
typedef _Must_inspect_result_
|
||||||
|
_Return_type_success_(return != NULL)
|
||||||
|
_Post_maybenull_
|
||||||
|
_Post_writable_byte_size_(PacketSize)
|
||||||
|
BYTE *(WINAPI WINTUN_ALLOCATE_SEND_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _In_ DWORD PacketSize);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends the packet and releases internal buffer. WintunSendPacket is thread-safe, but the WintunAllocateSendPacket
|
||||||
|
* order of calls define the packet sending order. This means the packet is not guaranteed to be sent in the
|
||||||
|
* WintunSendPacket yet.
|
||||||
|
*
|
||||||
|
* @param Session Wintun session handle obtained with WintunStartSession
|
||||||
|
*
|
||||||
|
* @param Packet Packet obtained with WintunAllocateSendPacket
|
||||||
|
*/
|
||||||
|
typedef VOID(WINAPI WINTUN_SEND_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _In_ const BYTE *Packet);
|
||||||
|
|
||||||
|
#pragma warning(pop)
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
186
install-nebula.ps1
Normal file
186
install-nebula.ps1
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
#Requires -RunAsAdministrator
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
# Verify running as Administrator
|
||||||
|
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
|
||||||
|
$principal = [Security.Principal.WindowsPrincipal]$identity
|
||||||
|
if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
|
||||||
|
Write-Error "This script must be run as a machine Administrator."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Definition
|
||||||
|
$InstallDir = "C:\Program Files\Nebula"
|
||||||
|
$ConfigPath = "$InstallDir\config.yml"
|
||||||
|
$ExpectedBinPath = "`"$InstallDir\nebula.exe`" -service run -config `"$ConfigPath`""
|
||||||
|
|
||||||
|
# --- Validate all required files before making any changes ---
|
||||||
|
|
||||||
|
$missing = @()
|
||||||
|
|
||||||
|
# Per-host files expected in the working directory
|
||||||
|
$HostFiles = @("config.yml", "host.crt", "host.key")
|
||||||
|
foreach ($file in $HostFiles) {
|
||||||
|
if (-not (Test-Path (Join-Path $PWD $file))) {
|
||||||
|
$missing += "$file (expected in working directory: $PWD)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Package files expected alongside the script
|
||||||
|
$PackageFiles = @("ca.crt", "nebula.exe")
|
||||||
|
foreach ($file in $PackageFiles) {
|
||||||
|
if (-not (Test-Path (Join-Path $ScriptDir $file))) {
|
||||||
|
$missing += "$file (expected in script directory: $ScriptDir)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (Test-Path (Join-Path $ScriptDir "dist"))) {
|
||||||
|
$missing += "dist\ (expected in script directory: $ScriptDir)"
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($missing.Count -gt 0) {
|
||||||
|
Write-Error "Missing required files:`n - $($missing -join "`n - ")"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Check if already installed and running correctly ---
|
||||||
|
|
||||||
|
$existing = Get-Service -Name "nebula" -ErrorAction SilentlyContinue
|
||||||
|
if ($existing) {
|
||||||
|
$svcConfig = sc.exe qc nebula 2>&1 | Out-String
|
||||||
|
$correctBinary = $svcConfig -match [regex]::Escape($ExpectedBinPath)
|
||||||
|
|
||||||
|
if ($existing.Status -eq "Running" -and $correctBinary) {
|
||||||
|
# Compare all source files against installed files by hash
|
||||||
|
$allMatch = $true
|
||||||
|
foreach ($file in $PackageFiles) {
|
||||||
|
$src = (Get-FileHash (Join-Path $ScriptDir $file)).Hash
|
||||||
|
$dst = (Get-FileHash (Join-Path $InstallDir $file) -ErrorAction SilentlyContinue).Hash
|
||||||
|
if ($src -ne $dst) { $allMatch = $false; break }
|
||||||
|
}
|
||||||
|
if ($allMatch) {
|
||||||
|
foreach ($file in $HostFiles) {
|
||||||
|
$src = (Get-FileHash (Join-Path $PWD $file)).Hash
|
||||||
|
$dst = (Get-FileHash (Join-Path $InstallDir $file) -ErrorAction SilentlyContinue).Hash
|
||||||
|
if ($src -ne $dst) { $allMatch = $false; break }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($allMatch) {
|
||||||
|
Write-Host "Nebula is already installed and running with matching files. No changes needed."
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "Nebula is running but files have changed. Reinstalling..."
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "Stopping existing Nebula service..."
|
||||||
|
if ($existing.Status -eq "Running") {
|
||||||
|
sc.exe stop nebula | Out-Null
|
||||||
|
# Wait for the service to fully stop
|
||||||
|
$timeout = 10
|
||||||
|
while ($timeout -gt 0) {
|
||||||
|
$svc = Get-Service -Name "nebula" -ErrorAction SilentlyContinue
|
||||||
|
if ($svc.Status -eq "Stopped") { break }
|
||||||
|
Start-Sleep -Seconds 1
|
||||||
|
$timeout--
|
||||||
|
}
|
||||||
|
if ($timeout -eq 0) {
|
||||||
|
Write-Error "Timed out waiting for Nebula service to stop."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sc.exe delete nebula | Out-Null
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error "Failed to remove existing Nebula service. A reboot may be required if it is marked for deletion."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
Start-Sleep -Seconds 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Create install directory ---
|
||||||
|
|
||||||
|
if (-not (Test-Path $InstallDir)) {
|
||||||
|
try {
|
||||||
|
New-Item -ItemType Directory -Path $InstallDir | Out-Null
|
||||||
|
} catch {
|
||||||
|
Write-Error "Failed to create install directory: $_"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Copy files ---
|
||||||
|
|
||||||
|
try {
|
||||||
|
Write-Host "Copying package files..."
|
||||||
|
Copy-Item (Join-Path $ScriptDir "ca.crt") $InstallDir -Force
|
||||||
|
Copy-Item (Join-Path $ScriptDir "nebula.exe") $InstallDir -Force
|
||||||
|
Copy-Item (Join-Path $ScriptDir "dist") $InstallDir -Recurse -Force
|
||||||
|
|
||||||
|
Write-Host "Copying host files..."
|
||||||
|
foreach ($file in $HostFiles) {
|
||||||
|
Copy-Item (Join-Path $PWD $file) $InstallDir -Force
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
Write-Error "Failed to copy files: $_"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Verify files landed in install directory ---
|
||||||
|
|
||||||
|
$AllFiles = $PackageFiles + $HostFiles
|
||||||
|
foreach ($file in $AllFiles) {
|
||||||
|
if (-not (Test-Path (Join-Path $InstallDir $file))) {
|
||||||
|
Write-Error "File copy verification failed: $file not found in $InstallDir"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (Test-Path (Join-Path $InstallDir "dist"))) {
|
||||||
|
Write-Error "File copy verification failed: dist\ not found in $InstallDir"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Install and configure service ---
|
||||||
|
|
||||||
|
Write-Host "Installing Nebula service..."
|
||||||
|
& "$InstallDir\nebula.exe" -service install -config "$ConfigPath"
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error "Failed to install Nebula service."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Wait for real network connectivity before starting, not just the TCP/IP stack
|
||||||
|
sc.exe config nebula depend= Tcpip/NlaSvc | Out-Null
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error "Failed to set service dependencies."
|
||||||
|
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
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error "Failed to set delayed-auto start."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Start and verify ---
|
||||||
|
|
||||||
|
Write-Host "Starting Nebula service..."
|
||||||
|
sc.exe start nebula | Out-Null
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error "Failed to start Nebula service. Check Event Viewer (Application log, source 'nebula')."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Start-Sleep -Seconds 3
|
||||||
|
|
||||||
|
$svc = Get-Service -Name "nebula"
|
||||||
|
if ($svc.Status -eq "Running") {
|
||||||
|
Write-Host "Nebula service is running."
|
||||||
|
} else {
|
||||||
|
Write-Error "Nebula service failed to start. Check Event Viewer (Application log, source 'nebula')."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
94
join-domain.ps1
Normal file
94
join-domain.ps1
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
#Requires -RunAsAdministrator
|
||||||
|
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory=$true)]
|
||||||
|
[string]$Domain,
|
||||||
|
|
||||||
|
[Parameter(Mandatory=$false)]
|
||||||
|
[string]$ComputerName
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
# Verify running as Administrator
|
||||||
|
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
|
||||||
|
$principal = [Security.Principal.WindowsPrincipal]$identity
|
||||||
|
if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
|
||||||
|
Write-Error "This script must be run as a machine Administrator."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Verify Nebula service is running
|
||||||
|
$nebulaSvc = Get-Service -Name "nebula" -ErrorAction SilentlyContinue
|
||||||
|
if (-not $nebulaSvc -or $nebulaSvc.Status -ne "Running") {
|
||||||
|
Write-Error "Nebula service is not running. Run install-nebula.ps1 first."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Validate computer name if provided
|
||||||
|
if ($ComputerName) {
|
||||||
|
if ($ComputerName.Length -gt 15) {
|
||||||
|
Write-Error "Computer name '$ComputerName' exceeds 15 character limit."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
if ($ComputerName -notmatch '^[a-zA-Z0-9-]+$') {
|
||||||
|
Write-Error "Computer name '$ComputerName' contains invalid characters. Use only letters, numbers, and hyphens."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if already joined to this domain
|
||||||
|
$cs = Get-WmiObject Win32_ComputerSystem
|
||||||
|
$alreadyJoined = $cs.PartOfDomain -and $cs.Domain -eq $Domain
|
||||||
|
if ($alreadyJoined) {
|
||||||
|
Write-Host "This machine is already joined to '$Domain'. No changes needed."
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Verify DNS can resolve the domain (i.e. DC is reachable)
|
||||||
|
try {
|
||||||
|
$resolved = Resolve-DnsName $Domain -ErrorAction Stop
|
||||||
|
Write-Host "Resolved $Domain to $($resolved[0].IPAddress)"
|
||||||
|
} catch {
|
||||||
|
Write-Error "Cannot resolve domain '$Domain'. Ensure DNS is pointed at the domain controller (run set-dns.ps1 first)."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Prompt for domain credentials
|
||||||
|
Write-Host "Enter credentials for a domain account with permission to join machines to '$Domain':"
|
||||||
|
$credential = Get-Credential -Message "Domain join credentials for $Domain"
|
||||||
|
if (-not $credential) {
|
||||||
|
Write-Error "No credentials provided."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Warn if computer name doesn't match expected name
|
||||||
|
if ($ComputerName -and $env:COMPUTERNAME -ne $ComputerName.ToUpper()) {
|
||||||
|
Write-Warning "Current computer name '$env:COMPUTERNAME' does not match '$ComputerName'."
|
||||||
|
Write-Warning "If you have a pre-staged AD object for '$ComputerName', rename this machine first and reboot before running this script."
|
||||||
|
Write-Warning "Proceeding with domain join as '$env:COMPUTERNAME'..."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Join the domain under the machine's current name
|
||||||
|
Write-Host "Joining '$Domain' as '$env:COMPUTERNAME'..."
|
||||||
|
try {
|
||||||
|
Add-Computer -DomainName $Domain -Credential $credential -Force
|
||||||
|
} catch {
|
||||||
|
Write-Error "Failed to join domain: $_"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Verify the join succeeded
|
||||||
|
$cs = Get-WmiObject Win32_ComputerSystem
|
||||||
|
if ($cs.PartOfDomain -and $cs.Domain -eq $Domain) {
|
||||||
|
Write-Host "Successfully joined '$Domain' as '$env:COMPUTERNAME'."
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "A restart is required for changes to take effect."
|
||||||
|
$restart = Read-Host "Restart now? (y/n)"
|
||||||
|
if ($restart -eq "y") {
|
||||||
|
Restart-Computer -Force
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Write-Error "Domain join could not be verified."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
BIN
nebula.exe
Normal file
BIN
nebula.exe
Normal file
Binary file not shown.
63
set-dns.ps1
Normal file
63
set-dns.ps1
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#Requires -RunAsAdministrator
|
||||||
|
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory=$true)]
|
||||||
|
[string]$DnsServer,
|
||||||
|
|
||||||
|
[Parameter(Mandatory=$false)]
|
||||||
|
[string]$InterfaceAlias
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
# Verify running as Administrator
|
||||||
|
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
|
||||||
|
$principal = [Security.Principal.WindowsPrincipal]$identity
|
||||||
|
if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
|
||||||
|
Write-Error "This script must be run as a machine Administrator."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Verify Nebula service is running
|
||||||
|
$nebulaSvc = Get-Service -Name "nebula" -ErrorAction SilentlyContinue
|
||||||
|
if (-not $nebulaSvc -or $nebulaSvc.Status -ne "Running") {
|
||||||
|
Write-Error "Nebula service is not running. Run install-nebula.ps1 first."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Validate DNS server is a valid IP
|
||||||
|
if (-not ($DnsServer -as [System.Net.IPAddress])) {
|
||||||
|
Write-Error "Invalid DNS server address: $DnsServer"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Default to the Nebula tunnel adapter — physical adapters should keep their local DNS
|
||||||
|
if ($InterfaceAlias) {
|
||||||
|
$adapters = Get-NetAdapter -Name $InterfaceAlias -ErrorAction SilentlyContinue
|
||||||
|
if (-not $adapters) {
|
||||||
|
Write-Error "Network adapter '$InterfaceAlias' not found."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$adapters = Get-NetAdapter -Name "nebula1" -ErrorAction SilentlyContinue
|
||||||
|
if (-not $adapters) {
|
||||||
|
Write-Error "Nebula adapter 'nebula1' not found. Is the Nebula service running?"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($adapter in $adapters) {
|
||||||
|
Write-Host "Setting DNS to $DnsServer on '$($adapter.Name)'..."
|
||||||
|
Set-DnsClientServerAddress -InterfaceIndex $adapter.ifIndex -ServerAddresses $DnsServer
|
||||||
|
}
|
||||||
|
|
||||||
|
# Verify
|
||||||
|
foreach ($adapter in $adapters) {
|
||||||
|
$dns = Get-DnsClientServerAddress -InterfaceIndex $adapter.ifIndex -AddressFamily IPv4
|
||||||
|
if ($dns.ServerAddresses -contains $DnsServer) {
|
||||||
|
Write-Host "Verified: '$($adapter.Name)' DNS is now $DnsServer"
|
||||||
|
} else {
|
||||||
|
Write-Error "Failed to verify DNS on '$($adapter.Name)'"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user