forked from M-Labs/wfvm
Compare commits
33 Commits
nix_flakes
...
master
Author | SHA1 | Date |
---|---|---|
sb10q | e811c7c5d8 | |
Astro | 8051ad647a | |
Astro | ec1c08956b | |
Astro | 7e09796a9b | |
Sebastien Bourdeauducq | 4dcd3699fe | |
Sebastien Bourdeauducq | bf681b20aa | |
Sebastien Bourdeauducq | 3cbddd7218 | |
Astro | 3694b0a9f2 | |
Astro | b9e261de6f | |
Astro | 285b33a674 | |
Astro | 9a92143337 | |
Astro | 16e041282f | |
Astro | bc24fd6a2b | |
Astro | af9218e652 | |
Astro | fe347240f5 | |
Astro | d2d9c7acf6 | |
Astro | 267b3eec44 | |
Astro | 1550caf442 | |
Astro | 598b311215 | |
Astro | 79c1685f89 | |
Astro | a3df68b61f | |
Astro | 6542c37863 | |
Astro | abd67ce2e6 | |
Astro | a6b677f564 | |
Astro | d8e9f9878b | |
Astro | 5ba57fb502 | |
Astro | dfdcf0f19a | |
Astro | 9845a99863 | |
Astro | 4f7aef8788 | |
Astro | c124cfc5dc | |
Astro | 3b87f787a8 | |
Astro | b33b71eec7 | |
Astro | c732c671d9 |
40
README.md
40
README.md
|
@ -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
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
|
@ -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
142
flake.nix
|
@ -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
|
|
||||||
'';
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
''
|
|
@ -0,0 +1,13 @@
|
||||||
|
{ pkgs ? import <nixpkgs> {} }:
|
||||||
|
|
||||||
|
pkgs.mkShell {
|
||||||
|
|
||||||
|
buildInputs = [
|
||||||
|
pkgs.go
|
||||||
|
];
|
||||||
|
|
||||||
|
shellHook = ''
|
||||||
|
unset GOPATH
|
||||||
|
'';
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
{ pkgs }:
|
||||||
|
|
||||||
|
{
|
||||||
|
makeWindowsImage = attrs: import ./win.nix ({ inherit pkgs; } // attrs);
|
||||||
|
layers = import ./layers { inherit pkgs; };
|
||||||
|
utils = import ./utils.nix { inherit pkgs; };
|
||||||
|
}
|
|
@ -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";
|
||||||
|
|
||||||
|
}
|
|
@ -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
|
||||||
|
'';
|
||||||
|
}
|
|
@ -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 ];
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
50
wfvm/win.nix
50
wfvm/win.nix
|
@ -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} &
|
||||||
|
|
Loading…
Reference in New Issue