diff --git a/ComputerList.csv b/ComputerList.csv deleted file mode 100755 index e165018..0000000 --- a/ComputerList.csv +++ /dev/null @@ -1,2 +0,0 @@ -MAC,CN -78:45:c4:04:37:ab,TSMS-NINE-DTPC \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..5a88acb --- /dev/null +++ b/README.md @@ -0,0 +1,89 @@ +# Claremont MakerSpace Windows 10 Deployment + +A mess of various stuff to install Windows 10 via the network, with automatic hostname assignment from [Snipe-IT](https://snipeitapp.com/) and installation of [Salt](saltproject.io) minion. +Domain join and rest of setup (installing applications, applying configuration, etc.) is done afterwords by Salt and group policy. + +## Usage + +In theory, just enable UEFI PXE Booting, plug into the `CMS Classroom` network, and select `PXE Boot (IPV4)` (or similar) as a boot option. + +- For computers with UEFI, but without UEFI PXE (for some reason...), you can make a USB drive to jumpstart the process by with `ipxe.usb` ([see below](#build-ipxe)) +- For computers without Ethernet, a USB Ethernet adapter can be used (I'm not sure which adapters work, but USB 3 seems to have added a more standardized interface, so is more likely to work) + - In theory, some WiFi adapters support PXE booting (and are supported by iPXE), but not the ones in the latops CMS has + +## Setup + +Somewhat based on [this helpful guide][pxe_win]. +Written for Debian 11. +Various hostnames and exact paths may need to be adjusted in provided files. +Also assumes UEFI booting, because Legacy booting is pretty deprecated at this point. + +### Enable PXE booting in DHCP server + +Depends pretty heavily on DHCP server, but in UniFi it's in `Settings -> Networks -> -> Advanced`. +See [previously mentioned guide][pxe_win] or [iPXE docs](https://ipxe.org/howto/dhcpd) for more details (but ignore the bit about chainloading, as we instead are using an embedded script). + +### Build custom [iPXE](https://ipxe.org/) + +1. Clone `git://git.ipxe.org/ipxe.git` +2. Copy [`embed.ipxe`](./embed.ipxe) to `src/` + - This allows for chainloading, without needing support from the DHCP server +3. Build PXE executable or usb image + +```sh +make bin-x86_64-efi/ipxe.usb EMBED=embed.ipxe SHELL="sh" # For USB drive +make bin-x86_64-efi/ipxe.efi EMBED=embed.ipxe SHELL="sh" # For PXE TFTP boot +``` + +4. `dd` `ipxe.usb` to a USB drive, if needed (for computers with UEFI, but not UEFI PXE) + +### TFTP (via `tftpd-hpa`) + +1. Install `tftpd-hpa` package +2. Copy [`tftp/main.ipxe`](./tftp/main.ipxe) (and `ipxe.efi`, if PXE booting) to `/srv/tftp` + - This doesn't actually need to be a menu; it could basically just be `chain http://:8081/poll/1/${mac} ||` +3. Copy [`tftp/tftpd.map`](./tftp/tftpd.map) to `/etc/`, and add `--map-file /etc/tftpd.map` to `TFTP_OPTIONS` in `/etc/default/tftpd-hpa` + - This is to work around some older UEFI PXE implementations, which add a 0xFF character (which they render as ÿ) after the file name for unclear reasons + +### Hostname script + +1. Install `python3` and `python3-requests` +2. Copy [`ipxe-set-hostname-from-serial.py`](./ipxe-set-hostname-from-serial.py) to `/usr/lib/cgi-bin` +3. [Generate a token from Snipe-IT](https://snipe-it.readme.io/reference/generating-api-tokens) and edit it into the script + +### [Shoelaces](https://github.com/thousandeyes/shoelaces) + +1. Set base URL, bind address, and data dir in `/etc/default/shoelaces` +2. Copy [`shoelaces`][./shoelaces] folder to `/srv/shoelaces` +3. Download [wimboot](https://ipxe.org/wimboot) and put it in `/srv/shoelaces/static/windows` +4. Adjust `mappings.yaml` for the targeted subnet + +### Windows Installer + +1. [Download The latest Windows 10 ISO](https://www.microsoft.com/en-us/software-download/windows10ISO) +2. Extract the ISO to `software` SMB share, at `pxe/Windows10` +3. Retrieve the following files for pxe booting, and place them in `/srv/shoelaces/static/windows/windows10/` (keeping the folder structure): + +``` +boot/bcd +boot/boot.sdi +sources/boot.wim +``` + +### [Salt](https://saltproject.io/) minion + +1. [Download latest salt windows minion installer](https://docs.saltproject.io/salt/install-guide/en/latest/topics/install-by-operating-system/windows.html) +2. Place in `/pxe/Windows10/sources/$OEM$$/$$/setup/scripts/` as `Salt-Minion-Setup.exe` + - This slightly magic path will place the files at `C:\Windows\Setup\Scripts` in the installed OS +3. Copy [`SetupComplete.cmd`](./SetupComplete.cmd) to same directory + - This will be run after setup is complete as `SYSTEM`, and installs salt with the configured hostname as the minion name + +## Sources/References + +- [iPXE boot Windows and Linux, using uefi.][pxe_win] +- [iPXE - open source boot firmware [docs]][ipxe_docs] +- [Snipe-IT API Reference][snipeit_api] + +[pxe_win]: https://rpi4cluster.com/pxe/win/ "iPXE boot Windows and Linux, using uefi." +[ipxe_docs]: https://ipxe.org/docs "iPXE - open source boot firmware [docs]" +[snipeit_api]: https://snipe-it.readme.io/reference/ "Snipe-IT API Reference" diff --git a/Set-ComputerName.ps1 b/Set-ComputerName.ps1 deleted file mode 100755 index 4a46ba9..0000000 --- a/Set-ComputerName.ps1 +++ /dev/null @@ -1,58 +0,0 @@ -# Look up computer name from mac address in a file -# Based on Set-ComputerName from: -# https://kristopherjturner.com/2017/02/01/automating-computer-naming-after-deploying-windows-10-images/ - -$FileName = "ComputerList.csv" - -$scriptPath = Split-Path $script:MyInvocation.MyCommand.Path - -$thisComputerMAC = Get-WmiObject win32_networkadapterconfiguration -Filter 'ipenabled = "true"' | Select-Object MACAddress -$ComputerList = Import-Csv -Path "$scriptPath\$FileName" - -# look up computer name by MAC address -ForEach ($Computer in $ComputerList) { - Write-Host "$($Computer.CN) $($Computer.MAC) $thisComputerMac" - If ($thisComputerMAC -match $Computer.MAC) { - Write-Host "$($Computer.CN) matches the localhost MAC Address: $thisComputerMAC" - $NewComputerName = $Computer.CN - } -} - -# if computer isn’t on list, ask for a name -If ($Null -eq $NewComputerName) { - Write-Host "Computer is not found in computer list." -ForegroundColor Red - $NewComputerName = Read-Host -Prompt "Please enter desired computer name then hit enter" -} - -$cred = New-Object System.Management.Automation.PsCredential("SAWTOOTH\DomainJoin", (ConvertTo-SecureString "REPLACE_WITH_PASSWORD" -AsPlainText -Force)) - -Write-Host "Waiting for RPC Service" -(Get-Service RpcSs).WaitForStatus("Running") - -# rename computer and join to domain -Write-Host "Computer will be renamed to $NewComputerName." -Rename-Computer -NewName $NewComputerName -Force -Verbose - -Write-Host "Waiting for RPC Service" -(Get-Service RpcSs).WaitForStatus("Running") - -While($True){ - try{ - Write-Host "Trying to Domain Join" - Add-Computer -ErrorAction Stop -Force -DomainName sawtooth.claremontmakerspace.org -Options JoinWithNewName,InstallInvoke -Credential $cred - break - } - catch{ - Write-Host $_ - Start-Sleep -Seconds 1 # wait for a seconds before next attempt. - } -} - -# Install Salt -Write-Host "Installing Salt with minion name: $NewComputerName." -\\ucs.sawtooth.claremontmakerspace.org\Software\Salt-Minion-3004.2-Py3-AMD64-Setup.exe /S /minion-name="$NewComputerName" - - -#Read-Host -Prompt "Press Enter to reboot" -#Start-Sleep -Seconds 30 -#Restart-Computer diff --git a/SetupComplete.cmd b/SetupComplete.cmd new file mode 100644 index 0000000..21774ff --- /dev/null +++ b/SetupComplete.cmd @@ -0,0 +1,3 @@ +FOR /F %%H IN ('hostname') DO C:\Windows\Setup\Scripts\Salt-Minion-Setup.exe /S /minion-name=%%H /master=salt.sawtooth.claremontmakerspace.org + +RMDIR /S /Q C:\Windows\Setup\Scripts\ diff --git a/embed.ipxe b/embed.ipxe new file mode 100644 index 0000000..c0c83b7 --- /dev/null +++ b/embed.ipxe @@ -0,0 +1,10 @@ +#!ipxe +dhcp && goto netboot || goto dhcperror + +:dhcperror +prompt --key s --timeout 10000 DHCP failed, hit 's' for the iPXE shell; reboot in 10 seconds && shell || reboot + +:netboot +#chain tftp://${next-server}/main.ipxe || +chain tftp://172.18.142.6/main.ipxe || +prompt --key s --timeout 10000 Chainloading failed, hit 's' for the iPXE shell; reboot in 10 seconds && shell || reboot diff --git a/ipxe-set-hostname-from-serial.py b/ipxe-set-hostname-from-serial.py new file mode 100755 index 0000000..95ea43b --- /dev/null +++ b/ipxe-set-hostname-from-serial.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 + +import os + +import requests + +SNIPEIT_TOKEN = "SET_ME" + + +def get_hostname_from_snipeit(serial: str): + r = requests.get( + "https://inventory.claremontmakerspace.org/api/v1/hardware/byserial/" + serial, + headers = {"Authorization": "Bearer " + SNIPEIT_TOKEN} + ) + data = r.json() + if len(data["rows"]) == 1: + name = data["rows"][0]["name"] + if name: + return name + else: + asset_tag = data["rows"][0]["asset_tag"] + id = data["rows"][0]["id"] + raise Exception(f"No name set for asset {id}, tag {asset_tag}") + elif len(data["rows"]) < 1: + raise Exception("No asset found") + else: + raise Exception("Multiple assets with this serial number found") + + +print("Content-type: text/plain\n") +print("#!ipxe") +try: + hostname = get_hostname_from_snipeit(os.environ["QUERY_STRING"]) + if hostname: + print("set hostname " + hostname) + print("echo Set hostname=${hostname}") +except Exception as e: + print("echo Failed to set hostname from serial ${serial}, exiting in 5 seconds...") + print(f"echo Error: {e}") diff --git a/shoelaces/shoelaces/ipxe/windows10.ipxe.slc b/shoelaces/shoelaces/ipxe/windows10.ipxe.slc new file mode 100644 index 0000000..e2834b5 --- /dev/null +++ b/shoelaces/shoelaces/ipxe/windows10.ipxe.slc @@ -0,0 +1,15 @@ +{{define "windows10.ipxe" -}} +#!ipxe + +chain http://cms-net-svcs.claremontmakerspace.org/cgi-bin/ipxe-set-hostname-from-serial.py?${serial} + +kernel http://{{.baseURL}}/configs/static/windows/wimboot +initrd http://{{.baseURL}}/configs/static/windows/windows10/boot/bcd BCD +initrd http://{{.baseURL}}/configs/static/windows/windows10/boot/boot.sdi boot.sdi +initrd http://{{.baseURL}}/configs/static/windows/windows10/winpeshl.ini winpeshl.ini +initrd http://{{.baseURL}}/configs/windows10/unattend.xml?hostname=${hostname} unattend.xml +initrd http://{{.baseURL}}/configs/static/windows/windows10/install.bat install.bat +initrd http://{{.baseURL}}/configs/static/windows/windows10/sources/boot.wim boot.wim + +boot +{{end}} \ No newline at end of file diff --git a/shoelaces/shoelaces/mappings.yaml b/shoelaces/shoelaces/mappings.yaml new file mode 100644 index 0000000..b29d6b6 --- /dev/null +++ b/shoelaces/shoelaces/mappings.yaml @@ -0,0 +1,7 @@ +networkMaps: + - network: 172.18.57.0/24 + script: + name: windows10.ipxe + # - network: 172.18.57.0/24 + # script: + # name: hostname.ipxe diff --git a/shoelaces/shoelaces/static/windows/windows10/install.bat b/shoelaces/shoelaces/static/windows/windows10/install.bat new file mode 100644 index 0000000..06d23b1 --- /dev/null +++ b/shoelaces/shoelaces/static/windows/windows10/install.bat @@ -0,0 +1,9 @@ +wpeinit +ipconfig +echo "Sleeping for 10 seconds for network..." +echo wsh.sleep 10000 > sleep.vbs +cscript /nologo sleep.vbs +ipconfig +net use \\ucs\software +\\ucs\software\pxe\Windows10\setup.exe /unattend:unattend.xml +pause diff --git a/shoelaces/shoelaces/static/windows/windows10/winpeshl.ini b/shoelaces/shoelaces/static/windows/windows10/winpeshl.ini new file mode 100644 index 0000000..4f52a6a --- /dev/null +++ b/shoelaces/shoelaces/static/windows/windows10/winpeshl.ini @@ -0,0 +1,2 @@ +[LaunchApps] +"install.bat" \ No newline at end of file diff --git a/autounattend.xml b/shoelaces/shoelaces/windows10/unattend.xml.slc old mode 100755 new mode 100644 similarity index 80% rename from autounattend.xml rename to shoelaces/shoelaces/windows10/unattend.xml.slc index e03baca..5a79d4d --- a/autounattend.xml +++ b/shoelaces/shoelaces/windows10/unattend.xml.slc @@ -1,176 +1,143 @@ - - - - - - en-US - - 0409:00000409 - en-US - en-US - en-US - en-US - - - - - 0 - true - - - - 1 - Primary - 300 - - - - 2 - EFI - 100 - - - - 3 - MSR - 128 - - - - 4 - Primary - true - - - - - - 1 - 1 - - NTFS - DE94BBA4-06D1-4D40-A16A-BFD50179D6AC - - - - 2 - 2 - - FAT32 - - - - 3 - 3 - - - - 4 - 4 - - C - NTFS - - - - - - - - 0 - 4 - - false - - - - - - - VK7JG-NPHTM-C97JM-9MPGT-3V66T - Never - - true - - Claremont MakerSpace - - - - - - 1 - - - - - 0409:00000409 - en-US - en-US - en-US - en-US - - - true - - - 0 - - - * - W269N-WFGWX-YVC9B-4J6C9-T83GX - - - - - - true - true - true - true - true - true - 1 - - - - - - REPLACE_WITH_LOCAL_ADMIN_PASSWORD - true</PlainText> - </Password> - <Description>Local Administrator</Description> - <DisplayName>Local Administrator</DisplayName> - <Group>Administrators;Power Users</Group> - <Name>LocalAdmin</Name> - </LocalAccount> - </LocalAccounts> - </UserAccounts> - <RegisteredOrganization></RegisteredOrganization> - <RegisteredOwner></RegisteredOwner> - <DisableAutoDaylightTimeSet>false</DisableAutoDaylightTimeSet> - <TimeZone>Eastern Standard Time</TimeZone> - <AutoLogon> - <Enabled>true</Enabled> - <LogonCount>2</LogonCount> - <Username>LocalAdmin</Username> - <Password> - <Value>REPLACE_WITH_LOCAL_ADMIN_PASSWORD</Value> - </Password> - </AutoLogon> - <FirstLogonCommands> - <SynchronousCommand wcm:action="add"> - <CommandLine>net use \\ucs\software /user:DomainJoin REPLACE_WITH_PASSWORD</CommandLine> - <Description>Set Up UCS Share</Description> - <Order>1</Order> - </SynchronousCommand> - <SynchronousCommand wcm:action="add"> - <CommandLine>powershell -NoExit -executionPolicy Bypass -File \\ucs.sawtooth.claremontmakerspace.org\Software\unattend\Set-ComputerName.ps1</CommandLine> - <Description>Set Computer Name</Description> - <Order>2</Order> - <RequiresUserInput>true</RequiresUserInput> - </SynchronousCommand> - </FirstLogonCommands> - </component> - </settings> - <cpi:offlineImage cpi:source="wim:d:/sources/install.wim#Windows 10 Pro" xmlns:cpi="urn:schemas-microsoft-com:cpi" /> -</unattend> +{{define "windows10/unattend.xml" -}} +<?xml version="1.0" encoding="utf-8"?> +<unattend xmlns="urn:schemas-microsoft-com:unattend"> + <settings pass="windowsPE"> + <component name="Microsoft-Windows-International-Core-WinPE" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <SetupUILanguage> + <UILanguage>en-US</UILanguage> + </SetupUILanguage> + <InputLocale>0409:00000409</InputLocale> + <SystemLocale>en-US</SystemLocale> + <UILanguage>en-US</UILanguage> + <UILanguageFallback>en-US</UILanguageFallback> + <UserLocale>en-US</UserLocale> + </component> + <component name="Microsoft-Windows-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <DiskConfiguration> + <Disk wcm:action="add"> + <DiskID>0</DiskID> + <WillWipeDisk>true</WillWipeDisk> + <CreatePartitions> + <!-- Windows RE Tools partition --> + <CreatePartition wcm:action="add"> + <Order>1</Order> + <Type>Primary</Type> + <Size>300</Size> + </CreatePartition> + <!-- System partition (ESP) --> + <CreatePartition wcm:action="add"> + <Order>2</Order> + <Type>EFI</Type> + <Size>100</Size> + </CreatePartition> + <!-- Microsoft reserved partition (MSR) --> + <CreatePartition wcm:action="add"> + <Order>3</Order> + <Type>MSR</Type> + <Size>128</Size> + </CreatePartition> + <!-- Windows partition --> + <CreatePartition wcm:action="add"> + <Order>4</Order> + <Type>Primary</Type> + <Extend>true</Extend> + </CreatePartition> + </CreatePartitions> + <ModifyPartitions> + <!-- Windows RE Tools partition --> + <ModifyPartition wcm:action="add"> + <Order>1</Order> + <PartitionID>1</PartitionID> + <Label>WINRE</Label> + <Format>NTFS</Format> + <TypeID>DE94BBA4-06D1-4D40-A16A-BFD50179D6AC</TypeID> + </ModifyPartition> + <!-- System partition (ESP) --> + <ModifyPartition wcm:action="add"> + <Order>2</Order> + <PartitionID>2</PartitionID> + <Label>System</Label> + <Format>FAT32</Format> + </ModifyPartition> + <!-- MSR partition does not need to be modified --> + <ModifyPartition wcm:action="add"> + <Order>3</Order> + <PartitionID>3</PartitionID> + </ModifyPartition> + <!-- Windows partition --> + <ModifyPartition wcm:action="add"> + <Order>4</Order> + <PartitionID>4</PartitionID> + <Label>OS</Label> + <Letter>C</Letter> + <Format>NTFS</Format> + </ModifyPartition> + </ModifyPartitions> + </Disk> + </DiskConfiguration> + <ImageInstall> + <OSImage> + <InstallTo> + <DiskID>0</DiskID> + <PartitionID>4</PartitionID> + </InstallTo> + <InstallToAvailablePartition>false</InstallToAvailablePartition> + </OSImage> + </ImageInstall> + <UserData> + <ProductKey> + <!-- Do not uncomment the Key element if you are using trial ISOs --> + <!-- You must uncomment the Key element (and optionally insert your own key) if you are using retail or volume license ISOs --> + <Key>VK7JG-NPHTM-C97JM-9MPGT-3V66T</Key> + <WillShowUI>Never</WillShowUI> + </ProductKey> + <AcceptEula>true</AcceptEula> + <FullName></FullName> + <Organization>Claremont MakerSpace</Organization> + </UserData> + </component> + </settings> + <settings pass="generalize"> + <component name="Microsoft-Windows-Security-SPP" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <SkipRearm>1</SkipRearm> + </component> + </settings> + <settings pass="specialize"> + <component name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <InputLocale>0409:00000409</InputLocale> + <SystemLocale>en-US</SystemLocale> + <UILanguage>en-US</UILanguage> + <UILanguageFallback>en-US</UILanguageFallback> + <UserLocale>en-US</UserLocale> + </component> + <component name="Microsoft-Windows-Security-SPP-UX" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <SkipAutoActivation>true</SkipAutoActivation> + </component> + <component name="Microsoft-Windows-SQMApi" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <CEIPEnabled>0</CEIPEnabled> + </component> + <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <ComputerName>{{.hostname}}</ComputerName> + <ProductKey>W269N-WFGWX-YVC9B-4J6C9-T83GX</ProductKey> + </component> + </settings> + <settings pass="oobeSystem"> + <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <OOBE> + <HideEULAPage>true</HideEULAPage> + <HideOEMRegistrationScreen>true</HideOEMRegistrationScreen> + <HideOnlineAccountScreens>true</HideOnlineAccountScreens> + <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE> + <SkipUserOOBE>true</SkipUserOOBE> + <SkipMachineOOBE>true</SkipMachineOOBE> + <ProtectYourPC>1</ProtectYourPC> + </OOBE> + <RegisteredOrganization>Claremont MakerSpace</RegisteredOrganization> + <RegisteredOwner>Claremont MakerSpace</RegisteredOwner> + <DisableAutoDaylightTimeSet>false</DisableAutoDaylightTimeSet> + <TimeZone>Eastern Standard Time</TimeZone> + </component> + </settings> + <cpi:offlineImage cpi:source="wim:d:/sources/install.wim#Windows 10 Pro" xmlns:cpi="urn:schemas-microsoft-com:cpi" /> +</unattend> +{{end}} diff --git a/tftp/ipxe.efi b/tftp/ipxe.efi new file mode 100755 index 0000000..41a1b2c Binary files /dev/null and b/tftp/ipxe.efi differ diff --git a/tftp/main.ipxe b/tftp/main.ipxe new file mode 100644 index 0000000..25dff42 --- /dev/null +++ b/tftp/main.ipxe @@ -0,0 +1,17 @@ +#!ipxe +:MENU +menu +item --gap -- ---------------- iPXE boot menu ---------------- +item shoelaces Shoelaces +item shell ipxe shell +choose --default return --timeout 5000 target && goto ${target} + +:shoelaces +chain http://cms-net-svcs.claremontmakerspace.org:8081/poll/1/${mac} || +goto MENU + +:shell +shell || +goto MENU + +autoboot diff --git a/tftp/tftpd.map b/tftp/tftpd.map new file mode 100644 index 0000000..1b3ac06 --- /dev/null +++ b/tftp/tftpd.map @@ -0,0 +1 @@ +rg (.*)ÿ$ \1 \ No newline at end of file