Compare commits

..

33 Commits

Author SHA1 Message Date
sb10q e811c7c5d8 README: windows 11 2024-06-11 13:14:36 +08:00
Astro 8051ad647a utils.nix: pass all attrs
Fixes passing baseRtc and other parameters
2023-06-26 15:48:34 +02:00
Astro ec1c08956b default.nix: remove superfluous parentheses 2023-06-26 15:48:18 +02:00
Astro 7e09796a9b Revert "remove configurable efi flag"
This reverts commit af9218e652.
2023-06-08 18:42:48 +02:00
Sebastien Bourdeauducq 4dcd3699fe add 'nix run' demo 2023-05-27 15:36:00 +08:00
Sebastien Bourdeauducq bf681b20aa remove MSVC from demo image
Usually broken due to Microsoft's war on reproducibility.
2023-05-27 15:15:51 +08:00
Sebastien Bourdeauducq 3cbddd7218 update nixpkgs 2023-05-27 15:15:33 +08:00
Astro 3694b0a9f2 windowsIso: 22H2v1 -> 22H2v2 2023-05-26 00:13:21 +02:00
Astro b9e261de6f layers msvc cache: update outputHash 2023-05-25 15:59:36 +02:00
Astro 285b33a674 autounattend: fix installation without productKey 2023-05-25 15:59:36 +02:00
Astro 9a92143337 autounattend: disable forced password expiry in autounattended install stage already 2023-05-25 15:59:36 +02:00
Astro 16e041282f add enableTpm 2023-05-25 15:59:36 +02:00
Astro bc24fd6a2b autounattend: remove more optional bypasses 2023-05-25 15:59:36 +02:00
Astro af9218e652 remove configurable efi flag
Win11 is EFI-only
2023-05-25 15:59:36 +02:00
Astro fe347240f5 enable secureboot 2023-05-25 15:59:36 +02:00
Astro d2d9c7acf6 layers.msvc: update bootstapper sha256 2023-05-25 15:59:36 +02:00
Astro 267b3eec44 autounattend: explicitly set the InstallFrom/Path
Apparently required for Win11.
2023-05-25 15:59:36 +02:00
Astro 1550caf442 autounattend: add Bypass*Check
At least the TPM2.0 is missing from our qemu run but Windows 11's error
message doesn't reveal what else is missing. It is therefore hard to
estimate the work to do this properly.
2023-05-25 15:59:36 +02:00
Astro 598b311215 windowsIso: Win10_21H2 -> Win11_22H2 2023-05-25 15:59:36 +02:00
Astro 79c1685f89 virtioWinIso: 0.1.185-2 -> 0.1.229-1 2023-05-23 21:48:23 +02:00
Astro a3df68b61f README: add flakes section 2022-10-13 21:41:23 +02:00
Astro 6542c37863 wfvm/demo-image: make buildable from flake 2022-10-13 21:41:03 +02:00
Astro abd67ce2e6 wfvm/layers: update msvc binary sha256 2022-10-13 21:39:56 +02:00
Astro a6b677f564 wfvm/win: upgrade windowsIso from 21H1 to 21H2 2022-10-13 21:39:33 +02:00
Astro d8e9f9878b wfvm/utils: update baseRtc 2022-10-12 21:29:11 +02:00
Astro 5ba57fb502 wfvm/win: pass format of backing image to qemu-img
required since qemu 6.1.0
2022-10-12 21:05:39 +02:00
Astro dfdcf0f19a flake.lock: Update
Flake lock file updates:

• Updated input 'nixpkgs':
    'github:nixos/nixpkgs/c5924154f000e6306030300592f4282949b2db6c' (2022-10-08)
  → 'github:nixos/nixpkgs/285e77efe87df64105ec14b204de6636fb0a7a27' (2022-10-11)
2022-10-12 19:05:56 +02:00
Astro 9845a99863 flake.nix: switch input back to nixos-unstable now that fix PRs have landed 2022-10-12 19:04:51 +02:00
Astro 4f7aef8788 wfvm/win: replace libguestfs with guestfs-tools
tools like virt-make-fs have been split off into a separate package.
2022-10-11 00:10:58 +02:00
Astro c124cfc5dc flakify 2022-10-11 00:10:32 +02:00
Astro 3b87f787a8 s/runCommandNoCC/runCommand/ 2022-10-05 16:46:27 +02:00
Astro b33b71eec7 wfvm/win: lower wimsplit size to 4070 MB for more headroom 2022-10-05 16:07:06 +02:00
Astro c732c671d9 wfvm/layers: update vs_Community.exe sha256 2022-10-05 16:06:43 +02:00
13 changed files with 294 additions and 183 deletions

View File

@ -6,7 +6,7 @@ WFVM
A Nix library to create and manage virtual machines running Windows, a medieval operating system found on most computers in 2020. The F stands for "Functional" or a four-letter word of your choice. A Nix library to create and manage virtual machines running Windows, a medieval operating system found on most computers in 2020. The F stands for "Functional" or a four-letter word of your choice.
* Reproducible - everything runs in the Nix sandbox with no tricks. * Reproducible - everything runs in the Nix sandbox with no tricks.
* Fully automatic, parameterizable Windows 10 installation. * Fully automatic, parameterizable Windows 11 installation.
* Uses QEMU with KVM. * Uses QEMU with KVM.
* Supports incremental installation (using "layers") of additional software via QEMU copy-on-write backing chains. For example, ``wfvm.makeWindowsImage { installCommands = [ wfvm.layers.anaconda3 ]; };`` gives you a VM image with Anaconda3 installed, and ``wfvm.makeWindowsImage { installCommands = [ wfvm.layers.anaconda3 wfvm.layers.msys2 ]; };`` gives you one with both Anaconda3 and MSYS2 installed. The base Windows installation and the Anaconda3 data are shared between both images, and only the MSYS2 installation is performed when building the second image after the first one has been built. * Supports incremental installation (using "layers") of additional software via QEMU copy-on-write backing chains. For example, ``wfvm.makeWindowsImage { installCommands = [ wfvm.layers.anaconda3 ]; };`` gives you a VM image with Anaconda3 installed, and ``wfvm.makeWindowsImage { installCommands = [ wfvm.layers.anaconda3 wfvm.layers.msys2 ]; };`` gives you one with both Anaconda3 and MSYS2 installed. The base Windows installation and the Anaconda3 data are shared between both images, and only the MSYS2 installation is performed when building the second image after the first one has been built.
* Included layers: Anaconda3, a software installer chock full of bugs that pretends to be a package manager, Visual Studio, a spamming system for Microsoft accounts that includes a compiler, and MSYS2, which is the only sane component in the whole lot. * Included layers: Anaconda3, a software installer chock full of bugs that pretends to be a package manager, Visual Studio, a spamming system for Microsoft accounts that includes a compiler, and MSYS2, which is the only sane component in the whole lot.
@ -28,14 +28,21 @@ How to use
Install a Windows image Install a Windows image
----------------------- -----------------------
1. Adjust demo-image in ``flake.nix`` accordingly 1. Adjust demo-image.nix accordingly
2. Run: 2. Run:
If in impure mode
```shell ```shell
nix build .#demo-image nix-build demo-image.nix
./result ./result
``` ```
Results in a file called c.img
If in pure mode
```shell
nix-build demo-image.nix
ls -la ./result
```
Results in a symlink to the image in the nix store Results in a symlink to the image in the nix store
@ -46,4 +53,29 @@ Sometimes it can be useful to build the image _outside_ of the Nix sandbox for d
For this purpose we have an attribute called `impureMode` which outputs the shell script used by Nix inside the sandbox to build the image. For this purpose we have an attribute called `impureMode` which outputs the shell script used by Nix inside the sandbox to build the image.
When building an image with flakes, use ``nix build .#demo-image-impure`` instead.
Usage with Nix Flakes
---------------------
Build the demo by running:
```shell
nix build .#demoImage
```
This project's **flake.nix** exposes its functions under `lib`. To use
in your own project, setup your flake like this:
```nix
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
wfvm.url = "git+https://git.m-labs.hk/m-labs/wfvm";
};
outputs = { self, nixpkgs, wfvm }: {
packages."x86_64-linux".flaky-os = wfvm.lib.makeWindowsImage {
# configuration parameters go here
};
};
}
```

View File

@ -2,17 +2,17 @@
"nodes": { "nodes": {
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1593034146, "lastModified": 1685004253,
"narHash": "sha256-EypP7RyPq5Yv05VgsQoIkdn26KJUIgQCItHgVY1MMQE=", "narHash": "sha256-AbVL1nN/TDicUQ5wXZ8xdLERxz/eJr7+o8lqkIOVuaE=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "f8248ab6d9e69ea9c07950d73d48807ec595e923", "rev": "3e01645c40b92d29f3ae76344a6d654986a91a91",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "NixOS", "owner": "NixOS",
"ref": "nixos-23.05",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "f8248ab6d9e69ea9c07950d73d48807ec595e923",
"type": "github" "type": "github"
} }
}, },

142
flake.nix
View File

@ -1,128 +1,36 @@
{ {
description = "A Nix library to create and manage virtual machines running Windows."; description = "WFVM: Windows Functional Virtual Machine";
inputs.nixpkgs.url = github:NixOS/nixpkgs/f8248ab6d9e69ea9c07950d73d48807ec595e923;
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.05";
};
outputs = { self, nixpkgs }: outputs = { self, nixpkgs }:
let let
pkgs = import nixpkgs { system = "x86_64-linux"; }; # only x64 is supported
lib = pkgs.lib; system = "x86_64-linux";
# utils
utils = (import wfvm/utils.nix { inherit pkgs; });
# layers
layers = (import wfvm/layers { inherit pkgs; }); # end of layers
pkgs = nixpkgs.legacyPackages.${system};
in rec {
lib = import ./wfvm {
inherit pkgs;
};
# makeWindowsImage packages.${system} = rec {
makeWindowsImage = attrs: import wfvm/win.nix ({ inherit pkgs; } // attrs ); demoImage = import ./wfvm/demo-image.nix {
inherit self;
build-demo-image = { impureMode ? false }: makeWindowsImage {
# Build install script & skip building iso
inherit impureMode;
# Custom base iso
# windowsImage = pkgs.requireFile rec {
# name = "Win10_21H1_English_x64.iso";
# sha256 = "1sl51lnx4r6ckh5fii7m2hi15zh8fh7cf7rjgjq9kacg8hwyh4b9";
# message = "Get ${name} from https://www.microsoft.com/en-us/software-download/windows10ISO";
# };
# impureShellCommands = [
# "powershell.exe echo Hello"
# ];
# User accounts
# users = {
# artiq = {
# password = "1234";
# # description = "Default user";
# # displayName = "Display name";
# groups = [
# "Administrators"
# ];
# };
# };
# Auto login
# defaultUser = "artiq";
# fullName = "M-Labs";
# organization = "m-labs";
# administratorPassword = "12345";
# Imperative installation commands, to be installed incrementally
installCommands = if impureMode then [] else (with layers; [
(collapseLayers [
disable-autosleep
disable-autolock
disable-firewall
])
anaconda3 msys2 msvc msvc-ide-unbreak
]);
# services = {
# # Enable remote management
# WinRm = {
# Status = "Running";
# PassThru = true;
# };
# };
# License key (required)
# productKey = throw "Search the f* web"
# imageSelection = "1";
# Locales
# uiLanguage = "en-US";
# inputLocale = "en-US";
# userLocale = "en-US";
# systemLocale = "en-US";
}; };
in { default = lib.utils.wfvm-run {
name = "demo";
# bundle dev env image = demoImage;
devShell.x86_64-linux = pkgs.mkShell { script =
name = "wfvm-dev-shell"; ''
buildInputs = with pkgs; [ echo "Windows booted. Press Enter to terminate VM."
go read
]; '';
shellHook = '' display = true;
unset GOPATH
'';
};
inherit utils;
inherit makeWindowsImage;
inherit layers;
demo-ssh = utils.wfvm-run {
name = "demo-ssh";
image = build-demo-image {};
isolateNetwork = false;
script = ''
${pkgs.sshpass}/bin/sshpass -p1234 -- ${pkgs.openssh}/bin/ssh -p 2022 wfvm@localhost -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null
'';
};
packages.x86_64-linux = {
demo-image = build-demo-image {};
demo-image-impure = build-demo-image { impureMode = true; };
make-msys-packages = utils.wfvm-run {
name = "get-msys-packages";
image = makeWindowsImage { installCommands = [ layers.msys2 ]; };
script = ''
cat > getmsyspackages.bat << EOF
set MSYS=C:\\MSYS64
set TOOLPREF=mingw-w64-x86_64-
set PATH=%MSYS%\usr\bin;%MSYS%\mingw64\bin;%PATH%
pacman -Sp %TOOLPREF%gcc %TOOLPREF%binutils make autoconf automake libtool texinfo > packages.txt
EOF
\${utils.win-put}/bin/win-put getmsyspackages.bat
\${utils.win-exec}/bin/win-exec getmsyspackages
\${utils.win-get}/bin/win-get packages.txt
'';
}; };
}; };
}; };

View File

@ -15,7 +15,8 @@
, impureShellCommands ? [] , impureShellCommands ? []
, driveLetter ? "D:" , driveLetter ? "D:"
, efi ? true , efi ? true
, imageSelection ? "1" , imageSelection ? "Windows 11 Pro N"
, enableTpm
, ... , ...
}: }:
@ -58,18 +59,16 @@ let
assertCommand = c: builtins.typeOf c == "string" || builtins.typeOf c == "set" && builtins.hasAttr "Path" c && builtins.hasAttr "Description" c; assertCommand = c: builtins.typeOf c == "string" || builtins.typeOf c == "set" && builtins.hasAttr "Path" c && builtins.hasAttr "Description" c;
commands = builtins.map (x: assert assertCommand x; if builtins.typeOf x == "string" then { Path = x; Description = x; } else x) ( commands = builtins.map (x: assert assertCommand x; if builtins.typeOf x == "string" then { Path = x; Description = x; } else x) (
[ [ {
{ Path = "powershell.exe Set-ExecutionPolicy -Force Unrestricted";
Path = "powershell.exe Set-ExecutionPolicy -Force Unrestricted"; Description = "Allow unsigned powershell scripts.";
Description = "Allow unsigned powershell scripts."; } {
} Path = ''powershell.exe ${driveLetter}\win-bundle-installer.exe'';
] Description = "Install any declared packages.";
++ [ } {
{ Path = "net accounts /maxpwage:unlimited";
Path = ''powershell.exe ${driveLetter}\win-bundle-installer.exe''; Description = "Disable forced password expiry.";
Description = "Install any declared packages."; } ]
}
]
++ setupCommands ++ setupCommands
++ [ ++ [
{ {
@ -148,6 +147,14 @@ let
</DriverPaths> </DriverPaths>
</component> </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"> <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">
${lib.optionalString (!enableTpm) ''
<RunSynchronous>
<RunSynchronousCommand wcm:action="add">
<Order>1</Order>
<Path>reg add HKLM\System\Setup\LabConfig /v BypassTPMCheck /t reg_dword /d 0x00000001 /f</Path>
</RunSynchronousCommand>
</RunSynchronous>
''}
<DiskConfiguration> <DiskConfiguration>
<Disk wcm:action="add"> <Disk wcm:action="add">
@ -199,8 +206,9 @@ let
<PartitionID>3</PartitionID> <PartitionID>3</PartitionID>
</InstallTo> </InstallTo>
<InstallFrom> <InstallFrom>
<Path>\install.swm</Path>
<MetaData wcm:action="add"> <MetaData wcm:action="add">
<Key>/IMAGE/INDEX</Key> <Key>/IMAGE/NAME</Key>
<Value>${imageSelection}</Value> <Value>${imageSelection}</Value>
</MetaData> </MetaData>
</InstallFrom> </InstallFrom>
@ -209,7 +217,7 @@ let
<UserData> <UserData>
<ProductKey> <ProductKey>
${if productKey != null then "<Key>${productKey}</Key>" else ""} ${if productKey != null then "<Key>${productKey}</Key>" else "<Key/>"}
<WillShowUI>OnError</WillShowUI> <WillShowUI>OnError</WillShowUI>
</ProductKey> </ProductKey>
<AcceptEula>true</AcceptEula> <AcceptEula>true</AcceptEula>
@ -299,13 +307,13 @@ let
</component> </component>
</settings> </settings>
<cpi:offlineImage cpi:source="wim:c:/wim/windows-10/install.wim#${imageSelection}" xmlns:cpi="urn:schemas-microsoft-com:cpi" /> <cpi:offlineImage cpi:source="wim:c:/wim/windows-11/install.wim#${imageSelection}" xmlns:cpi="urn:schemas-microsoft-com:cpi" />
</unattend> </unattend>
''; '';
in { in {
# Lint and format as a sanity check # Lint and format as a sanity check
autounattendXML = pkgs.runCommandNoCC "autounattend.xml" {} '' autounattendXML = pkgs.runCommand "autounattend.xml" {} ''
${pkgs.libxml2}/bin/xmllint --format ${autounattendXML} > $out ${pkgs.libxml2}/bin/xmllint --format ${autounattendXML} > $out
''; '';

10
wfvm/bundle/default.nix Normal file
View File

@ -0,0 +1,10 @@
{ pkgs }:
pkgs.runCommand "win-bundle-installer.exe" {} ''
mkdir bundle
cd bundle
cp ${./go.mod} go.mod
cp ${./main.go} main.go
env HOME=$(mktemp -d) GOOS=windows GOARCH=amd64 ${pkgs.go}/bin/go build
mv bundle.exe $out
''

13
wfvm/bundle/shell.nix Normal file
View File

@ -0,0 +1,13 @@
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
buildInputs = [
pkgs.go
];
shellHook = ''
unset GOPATH
'';
}

7
wfvm/default.nix Normal file
View File

@ -0,0 +1,7 @@
{ pkgs }:
{
makeWindowsImage = attrs: import ./win.nix ({ inherit pkgs; } // attrs);
layers = import ./layers { inherit pkgs; };
utils = import ./utils.nix { inherit pkgs; };
}

82
wfvm/demo-image.nix Normal file
View File

@ -0,0 +1,82 @@
{ pkgs ? import <nixpkgs> {}
# Whether to generate just a script to start and debug the windows installation
, impureMode ? false
# Flake input `self`
, self ? null
}:
let
wfvm =
if self == null
# nix-build
then (import ./default.nix { inherit pkgs; })
# built from flake.nix
else self.lib;
in
wfvm.makeWindowsImage {
# Build install script & skip building iso
inherit impureMode;
# Custom base iso
# windowsImage = pkgs.requireFile rec {
# name = "Win11_22H2_English_x64v1.iso";
# sha256 = "08mbppsm1naf73z8fjyqkf975nbls7xj9n4fq0yp802dv1rz3whd";
# message = "Get disk image ${name} from https://www.microsoft.com/en-us/software-download/windows11/";
# };
# impureShellCommands = [
# "powershell.exe echo Hello"
# ];
# User accounts
# users = {
# artiq = {
# password = "1234";
# # description = "Default user";
# # displayName = "Display name";
# groups = [
# "Administrators"
# ];
# };
# };
# Auto login
# defaultUser = "artiq";
# fullName = "M-Labs";
# organization = "m-labs";
# administratorPassword = "12345";
# Imperative installation commands, to be installed incrementally
installCommands =
if impureMode
then []
else with wfvm.layers; [
(collapseLayers [
disable-autosleep
disable-autolock
disable-firewall
])
anaconda3 msys2
];
# services = {
# # Enable remote management
# WinRm = {
# Status = "Running";
# PassThru = true;
# };
# };
# License key (required)
# productKey = throw "Search the f* web"
imageSelection = "Windows 11 Pro N";
# Locales
# uiLanguage = "en-US";
# inputLocale = "en-US";
# userLocale = "en-US";
# systemLocale = "en-US";
}

13
wfvm/demo-ssh.nix Normal file
View File

@ -0,0 +1,13 @@
{ pkgs ? import <nixpkgs> {} }:
let
wfvm = (import ./default.nix { inherit pkgs; });
in
wfvm.utils.wfvm-run {
name = "demo-ssh";
image = import ./demo-image.nix { inherit pkgs; };
isolateNetwork = false;
script = ''
${pkgs.sshpass}/bin/sshpass -p1234 -- ${pkgs.openssh}/bin/ssh -p 2022 wfvm@localhost -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null
'';
}

View File

@ -1,7 +1,6 @@
{ pkgs }: { pkgs }:
let let
makeWindowsImage = attrs: import ../win.nix ({ inherit pkgs; } // attrs ); wfvm = import ../. { inherit pkgs; };
utils = import ../utils.nix { inherit pkgs; };
in in
{ {
anaconda3 = { anaconda3 = {
@ -73,20 +72,20 @@ in
bootstrapper = pkgs.fetchurl { bootstrapper = pkgs.fetchurl {
name = "RESTRICTDIST-vs_Community.exe"; name = "RESTRICTDIST-vs_Community.exe";
url = "https://aka.ms/vs/16/release/vs_community.exe"; url = "https://aka.ms/vs/16/release/vs_community.exe";
sha256 = "sha256-bxi8LsvNxSZshkTbhK/FEmMx84NKYB7TUNOm9sAKXS8="; sha256 = "sha256-l4ZKFZTgHf3BmD0eFWyGwsvb4lqB/LiQYizAABOs3gg=";
}; };
# This touchy-feely "community" piece of trash seems deliberately crafted to break Wine, so we use the VM to run it. # This touchy-feely "community" piece of trash seems deliberately crafted to break Wine, so we use the VM to run it.
download-vs = utils.wfvm-run { download-vs = wfvm.utils.wfvm-run {
name = "download-vs"; name = "download-vs";
image = makeWindowsImage { }; image = wfvm.makeWindowsImage { };
isolateNetwork = false; isolateNetwork = false;
script = script =
'' ''
ln -s ${bootstrapper} vs_Community.exe ln -s ${bootstrapper} vs_Community.exe
${utils.win-put}/bin/win-put vs_Community.exe ${wfvm.utils.win-put}/bin/win-put vs_Community.exe
rm vs_Community.exe rm vs_Community.exe
${utils.win-exec}/bin/win-exec "vs_Community.exe --quiet --norestart --layout c:\vslayout --add Microsoft.VisualStudio.Workload.NativeDesktop --includeRecommended --lang en-US" ${wfvm.utils.win-exec}/bin/win-exec "vs_Community.exe --quiet --norestart --layout c:\vslayout --add Microsoft.VisualStudio.Workload.NativeDesktop --includeRecommended --lang en-US"
${utils.win-get}/bin/win-get /c:/vslayout ${wfvm.utils.win-get}/bin/win-get /c:/vslayout
''; '';
}; };
cache = pkgs.stdenv.mkDerivation { cache = pkgs.stdenv.mkDerivation {
@ -94,7 +93,7 @@ in
outputHashAlgo = "sha256"; outputHashAlgo = "sha256";
outputHashMode = "recursive"; outputHashMode = "recursive";
outputHash = "0ic3jvslp2y9v8yv9mfr2mafkvj2q5frmcyhmlbxj71si1x3kpag"; outputHash = "sha256-GoOKzln8DXVMx52jWGEjwkOFkpSW+wEffAVmBVugIyk=";
phases = [ "buildPhase" ]; phases = [ "buildPhase" ];
buildInputs = [ download-vs ]; buildInputs = [ download-vs ];

View File

@ -2,7 +2,27 @@
set -e set -e
nix build .#make-msys-packages nix-build -E "
let
pkgs = import <nixpkgs> {};
wfvm = import ../default.nix { inherit pkgs; };
in
wfvm.utils.wfvm-run {
name = \"get-msys-packages\";
image = wfvm.makeWindowsImage { installCommands = [ wfvm.layers.msys2 ]; };
script = ''
cat > getmsyspackages.bat << EOF
set MSYS=C:\\MSYS64
set TOOLPREF=mingw-w64-x86_64-
set PATH=%MSYS%\usr\bin;%MSYS%\mingw64\bin;%PATH%
pacman -Sp %TOOLPREF%gcc %TOOLPREF%binutils make autoconf automake libtool texinfo > packages.txt
EOF
\${wfvm.utils.win-put}/bin/win-put getmsyspackages.bat
\${wfvm.utils.win-exec}/bin/win-exec getmsyspackages
\${wfvm.utils.win-get}/bin/win-get packages.txt
'';
}
"
./result/bin/wfvm-run-get-msys-packages ./result/bin/wfvm-run-get-msys-packages

View File

@ -1,23 +1,45 @@
{ pkgs, baseRtc ? "2020-04-20T14:21:42", cores ? "4", qemuMem ? "4G", efi ? true }: { pkgs
, baseRtc ? "2022-10-10T10:10:10"
, cores ? "4"
, qemuMem ? "4G"
, efi ? true
, enableTpm ? false
, ...
}:
rec { rec {
# qemu_test is a smaller closure only building for a single system arch # qemu_test is a smaller closure only building for a single system arch
qemu = pkgs.qemu; qemu = pkgs.qemu;
OVMF = pkgs.OVMF.override {
secureBoot = true;
};
mkQemuFlags = extraFlags: [ mkQemuFlags = extraFlags: [
"-enable-kvm" "-enable-kvm"
"-cpu host" "-cpu host"
"-smp ${cores}" "-smp ${cores}"
"-m ${qemuMem}" "-m ${qemuMem}"
"-M q35" "-M q35,smm=on"
"-vga qxl" "-vga qxl"
"-rtc base=${baseRtc}" "-rtc base=${baseRtc}"
"-device qemu-xhci" "-device qemu-xhci"
"-device virtio-net-pci,netdev=n1" "-device virtio-net-pci,netdev=n1"
] ++ pkgs.lib.optionals efi [ ] ++ pkgs.lib.optionals efi [
"-bios ${pkgs.OVMF.fd}/FV/OVMF.fd" "-bios ${OVMF.fd}/FV/OVMF.fd"
] ++ pkgs.lib.optionals enableTpm [
"-chardev" "socket,id=chrtpm,path=tpm.sock"
"-tpmdev" "emulator,id=tpm0,chardev=chrtpm"
"-device" "tpm-tis,tpmdev=tpm0"
] ++ extraFlags; ] ++ extraFlags;
tpmStartCommands = pkgs.lib.optionalString enableTpm ''
mkdir -p tpmstate
${pkgs.swtpm}/bin/swtpm socket \
--tpmstate dir=tpmstate \
--ctrl type=unixio,path=tpm.sock &
'';
# Pass empty config file to prevent ssh from failing to create ~/.ssh # Pass empty config file to prevent ssh from failing to create ~/.ssh
sshOpts = "-F /dev/null -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR -o ConnectTimeout=1"; sshOpts = "-F /dev/null -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR -o ConnectTimeout=1";
win-exec = pkgs.writeShellScriptBin "win-exec" '' win-exec = pkgs.writeShellScriptBin "win-exec" ''
@ -93,6 +115,7 @@ rec {
]); ]);
in pkgs.writeShellScriptBin "wfvm-run-${name}" '' in pkgs.writeShellScriptBin "wfvm-run-${name}" ''
set -e -m set -e -m
${tpmStartCommands}
${qemu}/bin/qemu-system-x86_64 ${pkgs.lib.concatStringsSep " " qemuParams} & ${qemu}/bin/qemu-system-x86_64 ${pkgs.lib.concatStringsSep " " qemuParams} &
${win-wait}/bin/win-wait ${win-wait}/bin/win-wait

View File

@ -5,17 +5,17 @@
, impureMode ? false , impureMode ? false
, installCommands ? [] , installCommands ? []
, users ? {} , users ? {}
, enableTpm ? true
# autounattend always installs index 1, so this default is backward-compatible # autounattend always installs index 1, so this default is backward-compatible
, imageSelection ? "Windows 10 Pro" , imageSelection ? "Windows 11 Pro N"
, efi ? true , efi ? true
, bundleInstaller ? {}
, ... , ...
}@attrs: }@attrs:
let let
lib = pkgs.lib; lib = pkgs.lib;
utils = import ./utils.nix { inherit pkgs efi; }; utils = import ./utils.nix ({ inherit pkgs efi enableTpm; } // attrs);
libguestfs = pkgs.libguestfs-with-appliance; inherit (pkgs) guestfs-tools;
# p7zip on >20.03 has known vulns but we have no better option # p7zip on >20.03 has known vulns but we have no better option
p7zip = pkgs.p7zip.overrideAttrs(old: { p7zip = pkgs.p7zip.overrideAttrs(old: {
@ -24,18 +24,8 @@ let
}; };
}); });
# bundle
bundleInstaller = pkgs.runCommandNoCC "win-bundle-installer.exe" {} ''
mkdir bundle
cd bundle
cp ${bundle/go.mod} go.mod
cp ${bundle/main.go} main.go
env HOME=$(mktemp -d) GOOS=windows GOARCH=amd64 ${pkgs.go}/bin/go build
mv bundle.exe $out
'';
runQemuCommand = name: command: ( runQemuCommand = name: command: (
pkgs.runCommandNoCC name { buildInputs = [ p7zip utils.qemu libguestfs ]; } pkgs.runCommand name { buildInputs = [ p7zip utils.qemu guestfs-tools ]; }
( (
'' ''
if ! test -f; then if ! test -f; then
@ -47,15 +37,14 @@ let
); );
windowsIso = if windowsImage != null then windowsImage else pkgs.requireFile rec { windowsIso = if windowsImage != null then windowsImage else pkgs.requireFile rec {
name = "xks67i4frg8k7rmlv5298aac0s4n5nih-RESTRICTDIST-release_svc_refresh_CLIENT_LTSC_EVAL_x64FRE_en-us.iso"; name = "Win11_22H2_English_x64v2.iso";
sha256 = "0fmw30g7959bh47z8xi5pmmxq4kb0sxs1qxf51il3xy2f2py33v6"; sha256 = "0xhhxy47yaf1jsfmskym5f65hljw8q0aqs70my86m402i6dsjnc0";
message = "Get ${name} from https://www.microsoft.com/en-us/software-download/windows10ISO"; message = "Get disk image ${name} from https://www.microsoft.com/en-us/software-download/windows11/";
}; };
# stable as of 2021-04-08
virtioWinIso = pkgs.fetchurl { virtioWinIso = pkgs.fetchurl {
url = "https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/archive-virtio/virtio-win-0.1.185-2/virtio-win-0.1.185.iso"; url = "https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/archive-virtio/virtio-win-0.1.229-1/virtio-win.iso";
sha256 = "11n3kjyawiwacmi3jmfmn311g9xvfn6m0ccdwnjxw1brzb4kqaxg"; sha256 = "1q5vrcd70kya4nhlbpxmj7mwmwra1hm3x7w8rzkawpk06kg0v2n8";
}; };
openSshServerPackage = pkgs.fetchurl { openSshServerPackage = pkgs.fetchurl {
@ -65,7 +54,7 @@ let
autounattend = import ./autounattend.nix ( autounattend = import ./autounattend.nix (
attrs // { attrs // {
inherit pkgs; inherit pkgs enableTpm;
users = users // { users = users // {
wfvm = { wfvm = {
password = "1234"; password = "1234";
@ -78,6 +67,8 @@ let
} }
); );
bundleInstaller = pkgs.callPackage ./bundle {};
# Packages required to drive installation of other packages # Packages required to drive installation of other packages
bootstrapPkgs = bootstrapPkgs =
runQemuCommand "bootstrap-win-pkgs.img" '' runQemuCommand "bootstrap-win-pkgs.img" ''
@ -118,7 +109,7 @@ let
'' ''
#!${pkgs.runtimeShell} #!${pkgs.runtimeShell}
set -euxo pipefail set -euxo pipefail
export PATH=${lib.makeBinPath [ p7zip utils.qemu libguestfs pkgs.wimlib ]}:$PATH export PATH=${lib.makeBinPath [ p7zip utils.qemu guestfs-tools pkgs.wimlib ]}:$PATH
# Create a bootable "USB" image # Create a bootable "USB" image
# Booting in USB mode circumvents the "press any key to boot from cdrom" prompt # Booting in USB mode circumvents the "press any key to boot from cdrom" prompt
@ -129,7 +120,7 @@ let
7z x -y ${windowsIso} -owin 7z x -y ${windowsIso} -owin
# Split image so it fits in FAT32 partition # Split image so it fits in FAT32 partition
wimsplit win/sources/install.wim win/sources/install.swm 4090 wimsplit win/sources/install.wim win/sources/install.swm 4070
rm win/sources/install.wim rm win/sources/install.wim
cp ${autounattend.autounattendXML} win/autounattend.xml cp ${autounattend.autounattendXML} win/autounattend.xml
@ -141,18 +132,20 @@ let
''} ''}
rm -rf win rm -rf win
${utils.tpmStartCommands}
# Qemu requires files to be rw # Qemu requires files to be rw
qemu-img create -f qcow2 c.img ${diskImageSize} qemu-img create -f qcow2 c.img ${diskImageSize}
qemu-system-x86_64 ${lib.concatStringsSep " " qemuParams} qemu-system-x86_64 ${lib.concatStringsSep " " qemuParams}
'' ''
); );
baseImage = pkgs.runCommandNoCC "RESTRICTDIST-windows.img" {} '' baseImage = pkgs.runCommand "RESTRICTDIST-windows.img" {} ''
${installScript} ${installScript}
mv c.img $out mv c.img $out
''; '';
finalImage = builtins.foldl' (acc: v: pkgs.runCommandNoCC "RESTRICTDIST-${v.name}.img" { finalImage = builtins.foldl' (acc: v: pkgs.runCommand "RESTRICTDIST-${v.name}.img" {
buildInputs = with utils; [ buildInputs = with utils; [
qemu win-wait win-exec win-put qemu win-wait win-exec win-put
] ++ (v.buildInputs or []); ] ++ (v.buildInputs or []);
@ -167,8 +160,11 @@ let
]); ]);
in '' in ''
set -x
${utils.tpmStartCommands}
# Create an image referencing the previous image in the chain # Create an image referencing the previous image in the chain
qemu-img create -f qcow2 -b ${acc} c.img qemu-img create -F qcow2 -f qcow2 -b ${acc} c.img
set -m set -m
qemu-system-x86_64 ${lib.concatStringsSep " " qemuParams} & qemu-system-x86_64 ${lib.concatStringsSep " " qemuParams} &