Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Build outputs
ssh_host*
sshpass
stage?-complete
vm-stage?
downloaded
temporary
*.qcow2
Expand Down
105 changes: 72 additions & 33 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@ driver_url=https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/arch

# Microsoft VM settings
vm_url=https://az792536.vo.msecnd.net/vms/VMBuild_20171019/VirtualBox/MSEdge/MSEdge.Win10.VirtualBox.zip
initial_pw=Passw0rd! # default password of the VM
port=22022 # host binding port for SSH to VM
# default password of the VM
initial_pw=Passw0rd!
# host binding port for SSH to VM
port=22022
rdpport=3389


# Configuration to be applied to the VM
hostname=win10
hostname=win10-openage
username=chantal
password=Passw0rd! # you probably want to override this...
password=wololo


# Shorthands
Expand All @@ -22,93 +25,129 @@ pubkey=$(privkey).pub


# Test for dependencies. Not sure if this is the right way to handle them.
# TODO: 7z, wget qemu
.PHONY: sshpass unzip 7z wget qemu-img qemu-system-x86_64
sshpass:
@which $@ > /dev/null || bash -c 'echo "$@ not found, please install it" && false'
touch $@

unzip:
@which $@ > /dev/null || bash -c 'echo "$@ not found, please install it" && false'
7z:
@which $@ > /dev/null || bash -c 'echo "$@ not found, please install it" && false'
wget:
@which $@ > /dev/null || bash -c 'echo "$@ not found, please install it" && false'
qemu-img:
@which $@ > /dev/null || bash -c 'echo "$@ not found, please install it" && false'
qemu-system-x86_64:
@which $@ > /dev/null || bash -c 'echo "$@ not found, please install it" && false'

# SSH hostkey generation. If not explicitly listed as target or aleady existent,
# the private key will be deleted automatically after ist is deployed to the VM.
$(privkey) $(pubkey):
# SSH hostkey generation.
$(privkey):
rm -f $(privkey) $(pubkey)
ssh-keygen -f $(privkey) -t ed25519 -N ''

$(pubkey): $(privkey)


# Download and unpacking section
$d:
mkdir $@

.PHONY: $d/virtio.iso
$d/virtio.iso:
$d/virtio.iso: | wget
# Google for RedHat Windows virtio drivers
wget -c -O $@ $(driver_url)
wget -c -O $@.tmp $(driver_url)
mv $@.tmp $@

$d/win10.zip: | $d
$d/win10.zip: | $d wget
# New URLS to be found at modern.ie
wget -c -O $@ $(vm_url)

$d/win10.ova: $d/win10.zip
$d/win10.ova: $d/win10.zip | unzip
unzip -p $^ > $@
rm $^

$d/win10.vmdk: $d/win10.ova
tar xOf $^ "MSEdge - Win10-disk001.vmdk" > $@
rm $^


# converted image
win10.qcow2: $d/win10.vmdk
win10.base.qcow2: $d/win10.vmdk | qemu-img
qemu-img convert -O qcow2 $^ $@

# overlay image
win10.qcow2: win10.base.qcow2 | qemu-img
qemu-img create -f qcow2 -b $^ $@

# helper files. cheap to create, will be automatically deleted when no longer needed
$t:
mkdir $@

$t/helper.qcow2: | $t
$t/helper.qcow2: | $t qemu-img
qemu-img create -f qcow2 $@ 1G

$t/ssh_host_ecdsa_key.pub: $d/win10.vmdk | $t
$t/vm_default_host_key.pub: $d/win10.vmdk | $t 7z
7z e -so $^ 'Program Files/OpenSSH/etc/ssh_host_ecdsa_key.pub' > $@


# Initial preparations in the VM. Includes changing hostname, username, password, disabling updates and installing viostor drivers
.ONESHELL: stage1-complete
stage1-complete: | $d/virtio.iso sshpass $t/ssh_host_ecdsa_key.pub $(pubkey) $(privkey) win10.qcow2 $t/helper.qcow2
set -m
echo "[localhost]:$(port) $$(cut -d' ' -f1,2 $t/ssh_host_ecdsa_key.pub)" > $t/known_host
.ONESHELL: vm-stage1
vm-stage1: | $d/virtio.iso $t/vm_default_host_key.pub $(pubkey) $(privkey) win10.qcow2 $t/helper.qcow2 qemu-system-x86_64 sshpass
@set -m
echo "[localhost]:$(port) $$(cut -d' ' -f1,2 $t/vm_default_host_key.pub)" > $t/known_host
echo Starting VM with virtio helper disk
qemu-system-x86_64 \
-drive file=win10.qcow2,if=ide \
-drive file=$t/helper.qcow2,if=virtio \
-drive file=$d/virtio.iso,media=cdrom \
-machine type=q35,accel=kvm \
-m 8G -smp cores=2,threads=1 \
-net nic -net user -redir tcp:$(port)::22 \
-vga std -display sdl &
while ! nc -z localhost $(port); do sleep 1; done
while ! sshpass -p 'Passw0rd!' ssh -p $(port) -o UserKnownHostsFile=$t/known_host -o ConnectTimeout=1 IEUser@localhost 'echo "Machine is up"' 2> /dev/null; do sleep 1; done
-net nic -net user,hostfwd=tcp::$(port)-:22 \
-vga std -display none &
echo Waiting for VM to respond on port 22
sleep 5
while ! sshpass -p '$(initial_pw)' ssh -p $(port) -o UserKnownHostsFile=$t/known_host -o ConnectTimeout=1 IEUser@localhost 'echo " Machine is up"' 2> /dev/null; do echo -n "."; sleep 1; done
sshpass -p '$(initial_pw)' ssh -p $(port) -o UserKnownHostsFile=$t/known_host IEUser@localhost 'mkdir -p /cygdrive/c/stage1'
sshpass -p '$(initial_pw)' scp -P $(port) -o UserKnownHostsFile=$t/known_host stage1.ps1 $(pubkey) $(privkey) IEUser@localhost:/cygdrive/c/stage1
sshpass -p '$(initial_pw)' ssh -p $(port) -o UserKnownHostsFile=$t/known_host IEUser@localhost 'powershell C:\\stage1\\stage1.ps1 -Hostname $(hostname) -Username $(username) -Password $(password)'
sshpass -p '$(initial_pw)' ssh -p $(port) -o UserKnownHostsFile=$t/known_host IEUser@localhost 'powershell C:\\stage1\\stage1.ps1 -Hostname $(hostname) -Username $(username) -Password $(password) -ResX 1920 -ResY 1080'
fg
touch $@

.PHONY: cleanvm
cleanvm:
rm -f vm-stage1 win10.qcow2


# This will install cmake, git, MSVC, vcpkg, python and all the other stuff.
# Since this is project specific, we should have this in a script.
# We don't touch stage2-complete to just use this as a run target right now.
.ONESHELL: stage2-complete
stage2-complete: stage1-complete | win10.qcow2 $(pubkey) $t
.ONESHELL: vm-stage2
vm-stage2: vm-stage1 | win10.qcow2 $(pubkey) $t qemu-system-x86_64
set -m
echo "[localhost]:$(port) $$(cut -d' ' -f1,2 $(pubkey))" > $t/known_host
qemu-system-x86_64 \
-drive file=win10.qcow2,if=virtio \
-machine type=q35,accel=kvm \
-m 8G -smp cores=2,threads=1 \
-net nic -net user -redir tcp:$(Port)::22 \
-net nic -net user,hostfwd=tcp::$(port)-:22 \
-vga std -display sdl &
fg
touch $@


run-headless: vm-stage1 | win10.qcow2 $(pubkey) $t qemu-system-x86_64
qemu-system-x86_64 \
-drive file=win10.qcow2,if=virtio \
-machine type=q35,accel=kvm \
-m 8G -smp cores=2,threads=1 \
-vga std -display none -net nic -net user,\
hostfwd=tcp::$(port)-:22,\
hostfwd=tcp::$(rdpport)-:3389,\
hostfwd=udp::$(rdpport)-:3389


.PHONY: run
run: vm-stage2
rm -f $^


.INTERMEDIATE: $d/win10.zip $d/win10.ova $d/win10.vmdk $d/virtio.iso $t/helper.qcow2 $(privkey) $t/ssh_host_ecdsa_key.pub
.INTERMEDIATE: $d/win10.zip $d/win10.ova $d/win10.vmdk $d/virtio.iso $t/helper.qcow2 $t/vm_default_host_key.pub $t/known_host

.PRECIOUS: $d/win10.vmdk $d/virtio.iso
.PRECIOUS: $d/win10.zip $d/win10.ova $d/win10.vmdk $d/virtio.iso
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,17 @@
# kevin-win-setup
Tools to automatically download and prepare a free Windows VM for kevin.

# How
``` bash
make run
```

You will be dropped in a Windows 10 VM after a few minutes. Downloads will be cached for later.

# Status
* VirtIO storage, autologin, display resolution and a bunch more work.
* Chantal has not yet been ported to Windows, need to find out what exactly is missing for that.
* Reference setup stages for Openage are being worked on.

# U w0t? A Makefile?
Well, why not. There are a lot operations that involve building files from other files. Also this was a godd enough excuse to learn about some less well known features of `make`.
55 changes: 47 additions & 8 deletions stage1.ps1
Original file line number Diff line number Diff line change
@@ -1,54 +1,93 @@
param(
[String]$Hostname = "win10",
[String]$Username = "chantal",
[String]$Password = "Passw0rd!"
[String]$Password = "wololo",
[Int]$ResX = 1920,
[Int]$ResY = 1080
)

$ScriptDir = Split-Path $MyInvocation.InvocationName

Start-Transcript "${ScriptDir}\stage1.log"

# Kill Windows Update and make sure it stays dead
Write-Host "Getting rid of Windows Update."
Stop-Service wuauserv
Set-Service wuauserv -StartupType Disabled

# Change hostname and user
Rename-Computer -NewName "${Hostname}"
# Disable autostart stuff
Write-Host "Disabling BGInfo."
Remove-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run -Name *

# User
Write-Host "Creating user '${Username}' with password '${Password}'."
$SecurePassword = ConvertTo-SecureString -String "${Password}" -AsPlainText -Force
New-LocalUser -Name "${Username}" -Password $SecurePassword
New-LocalUser -Name "${Username}" -Password $SecurePassword | Out-Null
Add-LocalGroupMember -Group Administrators -Member "${Username}"
Disable-LocalUser IEUser

Write-Host "Setting up automatic login for '${Username}'."
Set-ItemProperty "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\Winlogon" DefaultPassword "${Password}"
Set-ItemProperty "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\Winlogon" DefaultUserName "${Username}"

# Reconfigure OpenSSHd
Write-Host "Setting up SSH access."
$SID = Get-LocalUser $Username | select -ExpandProperty SID
$PasswdLine = "${Username}:*:197612:197121:U-${Hostname}\${Username},${SID}:/cygdrive/c/Users/${Username}:/bin/sh"

Push-Location "${env:programfiles}\OpenSSH\etc"
Add-Content -Path .\passwd "${PasswdLine}"
Remove-Item ssh_host*
Copy-Item "${ScriptDir}\ssh_host_*" .
Pop-Location

# Firewall
Write-Host "Setting up RDP access."
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server" -Name fDenyTSConnections -Value 0
Enable-NetFirewallRule -Name RemoteDesktop-UserMode-In-UDP
Enable-NetFirewallRule -Name RemoteDesktop-UserMode-In-TCP

# Install virtio storage driver
Write-Host "Installing virtio storage drivers."
# Thanks to https://stackoverflow.com/questions/36775331/extract-certificate-from-sys-file
$InfFile = "D:\viostor\w10\amd64\viostor.inf"
$SysFile = "D:\viostor\w10\amd64\viostor.sys"
$CerFile = "${ScriptDir}\RedHat.cer"
$ExportType = [System.Security.Cryptography.X509Certificates.X509ContentType]::Cert
$Cert = (Get-AuthenticodeSignature $SysFile).SignerCertificate
[System.IO.File]::WriteAllBytes($CerFile, $Cert.Export($ExportType))
Import-Certificate -FilePath "$CerFile" -CertStoreLocation Cert:\LocalMachine\TrustedPublisher
Import-Certificate -FilePath "$CerFile" -CertStoreLocation Cert:\LocalMachine\TrustedPublisher | Out-Null
Start-Process $InfFile -Verb Install
Start-Sleep 5

# Initialize virtio dummy disk, so that virtio will work for the boot drive
Write-Host "Preparing virtio storage drivers for boot."
Get-Disk | ?{ $_.PartitionStyle -eq "RAW" } | `
Initialize-Disk -PartitionStyle MBR -PassThru | `
New-Partition -AssignDriveLetter -UseMaximumSize | `
Format-Volume -FileSystem NTFS -NewFileSystemLabel "dummy" -Confirm:$false
Format-Volume -FileSystem NTFS -NewFileSystemLabel "dummy" -Confirm:$false | Out-Null

# We are done.
# Display resolution
Write-Host "Setting display resolution to ${ResX}x${ResY}."
Push-Location "HKLM:\SYSTEM\CurrentControlSet\Control\GraphicsDrivers\Configuration\MSBDD_NOEDID_1234_1111_00000000_00010000_0^FD62006E2425EAA7C207AF2974F7309B\00"
Set-ItemProperty -Path . -Name PrimSurfSize.cx -Value $ResX
Set-ItemProperty -Path . -Name PrimSurfSize.cy -Value $ResY
Set-ItemProperty -Path . -Name Stride -Value $(4 * $ResX)
Set-ItemProperty -Path 00 -Name PrimSurfSize.cx -Value $ResX
Set-ItemProperty -Path 00 -Name PrimSurfSize.cy -Value $ResY
Set-ItemProperty -Path 00 -Name Stride -Value $(4 * $ResX)
Set-ItemProperty -Path 00 -Name ActiveSize.cx -Value $ResX
Set-ItemProperty -Path 00 -Name ActiveSize.cy -Value $ResY
Set-ItemProperty -Path 00 -Name DwmClipBox.right -Value $ResX
Set-ItemProperty -Path 00 -Name DwmClipBox.bottom -Value $ResY
Set-ItemProperty -Path 00 -Name Flags -Value 0x830f8f
Pop-Location

# Hostname
Write-Host "Changing hostname to '${Hostname}'."
Rename-Computer -NewName "${Hostname}" | Out-Null

# Shutdown
Write-Host "Shutting VM down."
Start-Process -NoNewWindow "shutdown.exe" -ArgumentList "/s /t 0"

Stop-Transcript