首頁 > 軟體

在 FreeBSD bhyve 下執行 Windows

2020-06-16 17:48:18

正如我之前所寫的那樣,FreeBSD 下的虛擬機器管理程式 bhyve 即將支援 Windows 了。在其通過 FreeBSD Virtualization 郵寄清單公布時,FreeBSD 11.0-CURRENT r288524 版已對其正式支援。不久之後,Windows 的外交人員 Michael Dexter 就在 FreeBSD Wiki 上編寫了一個關於在 bhyve 下執行 Windows 的很棒的指引手冊。

在 bhyve 下執行 Windows 的秘密武器就是全新的 UEFI 支援。這真是個相當棒的訊息,因為當你在 bhyve 中採用 UEFI 時, 你已不必先在 bhyveload 或 grub-bhyve 中載入作業系統。下面是在命令列執行一個 Windows bhyve 範例的範例:

bhyve
      -c 2
      -s 0,hostbridge
      -s 3,ahci-hd,windows2016.img
      -s 4,ahci-cd,install.iso
      -s 10,virtio-net,tap0
      -s 31,lpc
      -l com1,/dev/nmdm0A
      -l com2,/dev/nmdm1A
      -l bootrom,BHYVE_UEFI_20151002.fd
      -m 2G -H -w
      windows2016

在我得業餘時間裡,我正在開發一個叫做 iohyve 小的 shell 指令碼,所以當此支援到來時,我欣喜若狂。我開始開發 iohyve 是因為當進行沙箱測試時,我不想使用 Oracle 的 VirtualBox。有時我喜歡點選一些不可靠的連結並寫郵件給濫用的部門並標記為垃圾郵件,我希望讓盡可能多的任何可能的潛在惡意軟體遠離我。由於大多數惡意軟體都是為 Windows 使用者所寫,在 bhyve 中支援它意味著我已經慢慢的接近實現這一目標了。現在 iohyve 還不能將客戶機放在優秀的虛擬 NAT 網路,但我最近已經把 UEFI 支援 加入到了 FreeBSD ports 中的 iohyve v0.7 版本了。對於今天這個教學,我將使用 GitHub 上的版本來完成在 FreeBSD 中建立一個 bhyve Windows 客戶機的任務,因為這個 iohyve UEFI 命令的版本的 bug 比較少。請注意,由於 UEFI 韌體還是很新的,是實驗性質的,所以 iohyve 對它的支援也是實驗性質的。現在的局限在於你只能有一個 HDD,一個虛擬 NIC(使用 tap 裝置),以及沒有支援 pass-through。其他作業系統若通過 bhyveload 或 grup-bhyve 命令啟動則可以支援多個虛擬 HDD,甚至是 passthrough 支援。

主機和安裝 ISO 準備。

為了利用在 bhyve 中啟動晶片(UEFI)的功能,你需要執行FreeBSD 11.0-CURRENT(至少是r288524版本)。自從 iohyve 使用了ZFS, 如果你還在執行 11-CURRENT,你真應該跳過它,使用 zpool 去設定。在我的筆電上,我選擇使用 Root on ZFS 來安裝FreeBSD,使用它非常簡單。稍後,我將會進入 iohyve 的設定。在這之前,我會先執行 bhyve,我需要先建立一個自定義的 Windows 安裝 ISO。因為 bhyve 不能輸出視訊,我們必須建立一個“無人值守的”安裝 ISO。因為我們會重新製作新的 ISO,我們將會在重新製作的 ISO 中安裝 VirtIO NIC 驅動來確保 Windows guest 能連線到網路。

正如我前面提到的,FreeBSD 的 wiki 條目 細節已經達到了這種程度。自從 bhyve 開始支援 Windows 之後,為了嘗試在 bhyve 下執行不同版本的 Windows 作業系統,我編寫了一系列小的指令碼和檔案。我發現自己一遍又一遍的嘗試不同的 AutoUnattend.xml 檔案。這個 XML 檔案基本上是當在裸機上安裝 Windows 時一系列點選按鈕的“響應”。我在一個被我稱為 Yabs 的指令碼裡簡化了這些流程。我會在本教學中使用那些指令碼,但我會隨時檢查並與 wiki 上的文章同步。你也需要找一個 Windows ISO 安裝檔案的副本。在本教學中,我將使用 Windows 2008,儘管我也已經用 Windows 2012 試過了。

-用 git 克隆 Yabs 的 repo,並開始你的初始工作目錄。

$ git clone https://github.com/pr1ntf/YetAnotherBhyveScript.git
Cloning into 'YetAnotherBhyveScript'...
remote: Counting objects: 22, done.
remote: Compressing objects: 100% (17/17), done.
remote: Total 22 (delta 9), reused 17 (delta 4), pack-reused 0
Unpacking objects: 100% (22/22), done.
Checking connectivity... done.$ mv YetAnotherBhyveScript/ win2k8auto$ cd win2k8auto/

-使用 fetch 命令從 Peter 那裡取得韌體,並從 Fedora Project 取得驅動。我將使用 0.1-94 版驅動,因為較新版的驅動在 Windows 2008 下不能正常工作。

$ fetch https://people.freebsd.org/~grehan/bhyve_uefi/BHYVE_UEFI_20151002.fd
$ fetch https://fedorapeople.org/groups/virt/virtio-win/deprecated-isos/archives/virtio-win-0.1-94/virtio-win-0.1-94.iso

-我找到的 ISO 安裝檔案叫做 Win2k8R2.iso,其目錄內容如下:

$ ls
BHYVE_UEFI_20151002.fd  Win2k8-AutoUnattend.xml extract.sh              remaster.sh             yabs.sh
README.txt              Win2k8R2.iso            null.iso                virtio-win-0.1-94.iso

-我們現在必須修改 extract.sh 指令碼來指向正確的位置。要注意執行 extract.sh 需要 FreeBSD 的 Port archivers/pz7ip。我的檔案如下:

#!/bin/sh
 
# Extract important stuff to remaster folder
 
folder=win2k8
iso=Win2k8R2.iso
drivers=virtio-win-0.1-94.iso
 
mkdir -p ${folder}/virtio
 
7z x ${iso} -o${folder}
 
tar xf $drivers -C ${folder}/virtio

-在我們執行 ./extract.sh 之後我們可以將 AutoUnattend.xml 檔案複製到我們的 win2k8 目錄下。Yabs 中包含的 AutoUnattend.xml 檔案會將管理員密碼設定為 R3dm0nd!,並會將預設網絡卡 IP 設定為 192.168.0.111 並具有指定閘道器和子網掩碼。因此一定要先對其進行修改。你可以在此檔案中刪除第二個 <SynchronousCommand wcm:action="add">。(在第一次登陸時執行這個命令 netsh interface ipv4 set address name="local area connection" source=static address=192.168.0.111 mask=255.255.255.0 gateway=192.168.0.1)。
 
$ cat Win2k8-AutoUnattend.xml
<?xml version="1.0" encoding="utf-8"?><unattend xmlns="urn:schemas-microsoft-com:unattend">
    <settings pass="windowsPE">
        <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>
                        <CreatePartition wcm:action="add">
                            <Order>1</Order>
                            <Size>400</Size>
                            <Type>EFI</Type>
                        </CreatePartition>
                        <CreatePartition wcm:action="add">
                            <Order>2</Order>
                            <Size>128</Size>
                            <Type>MSR</Type>
                        </CreatePartition>
                        <CreatePartition wcm:action="add">
                            <Order>3</Order>
                            <Extend>true</Extend>
                            <Type>Primary</Type>
                        </CreatePartition>
                    </CreatePartitions>
 
                    <ModifyPartitions>
                      <!-- EFI system partition (ESP) -->
                      <ModifyPartition wcm:action="add">
                        <Order>1</Order>
                        <PartitionID>1</PartitionID>
                        <Label>System</Label>
                        <Format>FAT32</Format>
                      </ModifyPartition>
 
                      <!-- Windows partition -->
                      <ModifyPartition wcm:action="add">
                        <Order>2</Order>
                        <PartitionID>3</PartitionID>
            </WindowsFeatures>
            <Themes>
              <ThemeName>Classic Theme</ThemeName>
              <DefaultThemesOff>true</DefaultThemesOff>
            </Themes>
            <ShowWindowsLive>false</ShowWindowsLive>
 
            <FirstLogonCommands>
              <SynchronousCommand wcm:action="add">
                  <CommandLine>cmd /C bcdedit /emssettings emsport:1 emsbaudrate:115200</CommandLine>
                  <Description>Enable EMS</Description>
                  <Order>1</Order>
              </SynchronousCommand>
              <SynchronousCommand wcm:action="add">
                    <CommandLine>netsh interface ipv4 set address name="local area connection" source=static
address=192.168.0.111 mask=255.255.255.0 gateway=192.168.0.1</CommandLine>
                    <Order>2</Order>
                </SynchronousCommand>
            </FirstLogonCommands>
        </component>
    </settings>
 
    <settings pass="offlineServicing">
      <component name="Microsoft-Windows-PnpCustomizationsNonWinPE" 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">
        <DriverPaths>
            <PathAndCredentials wcm:action="add" wcm:keyValue="1">
                <Path>d:virtio</Path>
            </PathAndCredentials>
        </DriverPaths>
      </component>
    </settings></unattend>$ cp Win2k8-AutoUnattend.xml win2k8/AutoUnattend.xml

-現在我們按照所有正確的位置修改 remaster.sh 指令碼來製作我們的無人值守 ISO 安裝檔案。執行 ./remaster.sh 後所有的東西都會打包在一個不錯的 ISO 檔案中。它需要 sysutils/cdrtools-devel port。我的如下:

#!/bin/sh
 
# Remaster new ISO
 
folder=win2k8
iso=win2k8.iso
 
mkisofs
    -b boot/etfsboot.com -no-emul-boot -c BOOT.CAT
    -iso-level 4 -J -l -D
    -N -joliet-long
    -relaxed-filenames -v
    -V "Custom" -udf
    -boot-info-table -eltorito-alt-boot -eltorito-platform 0xEF
    -eltorito-boot efi/microsoft/boot/efisys_noprompt.bin
    -no-emul-boot
    -o ${iso} ${folder}

iohyve 的安裝和準備

現在我們的工作目錄看起來應該是這樣的:

$ ls
BHYVE_UEFI_20151002.fd  Win2k8R2.iso            remaster.sh             win2k8.iso
README.txt              extract.sh              virtio-win-0.1-94.iso   yabs.sh
Win2k8-AutoUnattend.xml null.iso                win2k8

我們可以繼續安裝 iohyve 並為我們的 Windows 安裝過程做準備。如前所述,我們要使用 GitHub 上的版本,而不是 FreeBSD ports 中的版本。 執行 iohyve version 命令會輸出 0.7.1。

$ git clone https://github.com/pr1ntf/iohyve.git
Cloning into 'iohyve'...
remote: Counting objects: 904, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 904 (delta 0), reused 0 (delta 0), pack-reused 901
Receiving objects: 100% (904/904), 231.14 KiB | 0 bytes/s, done.
Resolving deltas: 100% (505/505), done.
Checking connectivity... done.$ cd iohyve/$ sudo make install clean
Password:
gzip -cn iohyve.8 > iohyve.8.gz
mkdir -p /usr/local/sbin
install -c -m 555 /usr/home/pr1ntf/iohyve/iohyve /usr/local/sbin/
install -c /usr/home/pr1ntf/iohyve/rc.d/* /usr/local/etc/rc.d/
install -c iohyve.8.gz /usr/local/man/man8/
rm -f iohyve.8.gz iohyve.8.cat.gz$ cd ~$ iohyve version
iohyve v0.7.1 2015/12/08 Im Here for the Party Edition

現在我們需要告訴 iohyve 三件事來設定它。

  • Zpool 儲存池儲存的 iohyve 資料集。 
  • 乙太網介面連線的所有 tap 裝置可以連線的寫死(當前)bridge0 裝置。
  • 告訴 iohyve 去載入 nmdm 及操作 iohyve 所需的 vmm 核心模組。

因為我們想要讓 iohyve 在我每次啟動電腦的時候都會設定網路和核心模組,我會將 iohyve 的設定分為兩部分:為 zpool 執行 iohyve setup pool=zroot 命令,然後修改主機上的 /etc/rc.conf 檔案並為介面和模組執行 service iohyve start 命令。

因為我的預設乙太網介面是 em0,於是我在主機的 /etc/rc.conf 檔案中新增了下面的幾行程式碼。

iohyve_enable="YES"
iohyve_flags="kmod=1 net=em0"

現在我可以執行 iohyve setup pool=zroot 命令和 service iohyve start 命令了:

$ sudo iohyve setup pool=zroot
Setting up iohyve pool...$ sudo service iohyve start
Starting iohyve guests...
Loading kernel modules...
Seting up bridge0 on em0...
net.link.tap.up_on_open: 1 -> 1

在我們可以建立我們的 Windows iohyve 客戶機之前,我們需要儘早在我們的 win2k8auto 資料夾中做以下三件事。

  •  最重要的是,我們需要一個秘密武器。我們需要 Peter 和 nahanni 已經提供給 FreeBSD 社群的 BHYVE_UEFI_20151002.fd UEFI 韌體檔案。
  • 我們需要我們早先已經建立的 win2k8.iso 無人值守 Windows ISO 安裝檔案。
  • 由於在這種方式中韌體與 Windows 互動時會出現一個異常行為,我們會需要一個基本上是空白光碟的 null.iso 檔案。

為了實現這一點,我們可以使用 iohyve cpiso 和 iohyve cpfw 命令:

$ ls
BHYVE_UEFI_20151002.fd  Win2k8R2.iso            remaster.sh             win2k8.iso
README.txt              extract.sh              virtio-win-0.1-94.iso   yabs.sh
Win2k8-AutoUnattend.xml null.iso                win2k8$ sudo iohyve cpiso win2k8.iso
Password:
Copying win2k8.iso from win2k8.iso...$ sudo iohyve cpiso null.iso
Copying null.iso from null.iso...$ sudo iohyve cpfw BHYVE_UEFI_20151002.fd
Copying BHYVE_UEFI_20151002.fd from BHYVE_UEFI_20151002.fd...$ iohyve isolist
Listing ISO's...
null.iso
win2k8.iso$ iohyve fwlist
Listing Firmware...
BHYVE_UEFI_20151002.fd

Windows 客戶機的建立和準備

首先我們可以使用 32G 大小的虛擬 HDD 建立一個叫做 win2k8 的新的客戶機。

$ sudo iohyve create win2k8 32G
Creating win2k8...

現在我們需要在客戶機上設定一些屬性,以便 bhyve 可以正常的啟動客戶機。

$ iohyve fwlist
Listing Firmware...
BHYVE_UEFI_20151002.fd$ sudo iohyve set win2k8 fw=BHYVE_UEFI_20151002.fd bargs="-H -w" ram=1024M
Setting win2k8 fw=BHYVE_UEFI_20151002.fd...
Setting win2k8 bargs=-H -w...
Setting win2k8 ram=1024M...$ iohyve getall win2k8
Getting win2k8 props...
description  Tue_Dec__8_10:30:51_MST_2015
fw          BHYVE_UEFI_20151002.fd
ram          1024M
os          default
cpu          1
size        32G
bargs        -H_-w
loader      bhyveload
name        win2k8
boot        0
tap          tap0
persist      1
con          nmdm0
autogrub    ninstall      yes
 

現在我們可以開始第一次引導安裝了。

Windows 安裝過程

Windows 的安裝過程分為三個主要的階段:         

  • 第一個安裝階段(從 ISO 安裝檔案中複製檔案)
  • 第二個安裝階段(Windows 實際解壓並安裝一些東西)
  • 第三個安裝階段(Windows 執行在 AutoUnattend.xml 中設定的“第一次登陸”命令)

在第一階段開始之前,我喜歡開啟另一個控制台並執行 iohyve console win2k8 命令。

$ sudo iohyve console win2k8
Password:
Starting console on win2k8...
~. to escape console [uses cu(1) for console]Connected

現在我們可以通過 iohyve uefi 命令開始安裝 win2k8 客戶機了。它應該會馬上開始。

$ iohyve isolist
Listing ISO's...
null.iso
win2k8.iso$ sudo iohyve uefi win2k8 win2k8.iso

切換到你的其他控制台,過一會你應該會看到 Windows SAC:
 
Computer is booting, SAC started and initialized.                             
 
Use the "ch -?" command for information about using channels.                 
Use the "?" command for general help.                                         
 
 
SAC>                       
EVENT: The CMD command is now available.                                       
SAC>                                                                           
EVENT:  A new channel has been created.  Use "ch -?" for channel help.       
Channel: SACSetupAct                                                           
SAC>                                                                           
EVENT:  A new channel has been created.  Use "ch -?" for channel help.       
Channel: SACSetupErr                                                           
SAC>

你可以按 [Esc]+[Tab] 鍵然後回車來切換到顯示了安裝細節的 SACSetupAct 控制台。Calling WIMApplyImage 的部分會需要執行一段時間才會退出。

:ProgramData] doesn't exist; no need to move it before applying image.2015-12-08 10:51:09, Info                  IBS    MoveOldOSFiles:File/folder [E:Recovery] doesn't exist; no need to move it before applying image.2015-12-08 10:51:09, Info                  IBS    MoveOldOSFiles:File/folder [E:Users] doesn't exist; no need to move it before applying image.2015-12-08 10:51:09, Info                  IBS    MoveOldOSFiles:File/folder [E:Windows] doesn't exist; no need to move it before applying image.2015-12-08 10:51:09, Info      [0x06412c] IBSLIB SetCheckpoint: Checkpoint("WinPEArchiveOldWindowsFoldersStartCheckpoint") in progress...2015-12-08 10:51:09, Info      [0x06412e] IBSLIB SetCheckpoint: Checkpoint "WinPEArchiveOldWindowsFoldersStartCheckpoint" successfully set.2015-12-08 10:51:09, Info      [0x06412c] IBSLIB SetCheckpoint: Checkpoint("WinPEArchiveOldWindowsFoldersDoneCheckpoint") in progress...2015-12-08 10:51:09, Info      [0x06412e] IBSLIB SetCheckpoint: Checkpoint "WinPEArchiveOldWindowsFoldersDoneCheckpoint" successfully set.2015-12-08 10:51:09, Info      [0x06412c] IBSLIB SetCheckpoint: Checkpoint("WinPEImageApplyReadyCheckpoint") in progress...2015-12-08 10:51:09, Info      [0x06412e] IBSLIB SetCheckpoint: Checkpoint "WinPEImageApplyReadyCheckpoint" successfully set.2015-12-08 10:51:09, Info      [0x06009e] IBS    DeployWIMImage:Calling IDepWIMImageResolved::Apply...2015-12-08 10:51:10, Info      [0x0606cc] IBS    Calling WIMApplyImage (flags = 0x184)...

安裝程式之後會繼續將一些驅動程式檔案複製到虛擬 HDD 中,之後你可以看到螢幕停在了下面的資訊。需要注意的是,如果它似乎被凍結在了那裡,那麼客戶機可能已經完成了它的第一個階段安裝並關機了。

Name:                  SACDescription:          Special AdminisType:                  VT-UTF8Channel GUID:          8472e3a1-9ddc-11e5-9c07-806e6f6e6963Application Type GUID: 63d02270-8aa4-11d5-bccf-806d6172696fPress <esc><tab> for next channel.Press <esc><tab>0 to return to the SAC channel.Use any other key to view this channel.

在第一個控制台上面,檢查並確保客戶機關機了。
 
$ iohyve list
Guest  VMM?  Running?  rcboot?  Description
win2k8  YES  NO        NO      Tue_Dec__8_10:30:51_MST_2015

從上面的資訊我們可以看到 Running? 標誌被設定為了 NO,這意味著客戶機已經被關閉了。我們還需要執行 iohyve destroy win2k8 來從 VMM? 中移除它,不然客戶機將在第二個安裝階段失敗。
 
$ sudo iohyve destroy win2k8
Destroying win2k8...errno = 37

現在我們可以執行第二個階段了:
 
$ sudo iohyve uefi win2k8 null.iso

如果你切換到了 SAC 控制台你可以改變 SACSetupAct 引導以及檢視螢幕上滾過的東西。這個步驟是非常迅速的,而且有時候你並不能捕捉到 SACSetupAct 引導,所以當客戶機關閉的時候你只能留下一個看起來如下的螢幕。
 
Setup is updating registry settings...
EVENT:  A new channel has been created.  Use "ch -?" for channel help.
Channel: SACSetupAct
SAC>
EVENT:  A new channel has been created.  Use "ch -?" for channel help.
Channel: SACSetupErr
SAC>
EVENT: The CMD command is now available.
SAC>
EVENT:  A channel has been closed.
Channel: SACSetupAct
SAC>
EVENT:  A channel has been closed.
Channel: SACSetupErr
SAC>
The SAC will become unavailable soon.  The computer is shutting down.
 
SAC>

切換到你的第一個控制,檢查以確保其再次關閉,並再次移除它。然後,你可以開始第三個階段的安裝,並最終第一次啟動 Windows。
 
$ iohyve list
Guest  VMM?  Running?  rcboot?  Description
win2k8  YES  NO        NO      Tue_Dec__8_10:30:51_MST_2015$ sudo iohyve destroy win2k8
Destroying win2k8...errno = 37$ sudo iohyve uefi win2k8 null.iso

第一次登陸安裝階段確實需要相當多的時間來完成,但最終,你的螢幕上會顯示如下資訊:

Computer is booting, SAC started and initialized.                             
 
Use the "ch -?" command for information about using channels.                 
Use the "?" command for general help.                                         
 
 
SAC>                                                                           
EVENT: The CMD command is now available.                                       
SAC>
 

 如果我執行 SAC 命令 i,我可以看到在之前的 AutoUnattend.xml 檔案中設定的客戶機的 IP 地址。預設情況下,Windows 客戶機無法 ping 通網路,但執行 nmap 192.168.0.111 將會顯示 Windows 遠端桌面可用。

SAC>i                                                                         
Net: 12, Ip=192.168.0.111  Subnet=255.255.255.0  Gateway=192.168.0.1

就是這樣!你現在可以用你慣用的遠端桌面用戶端使用你在 AutoUnattend.xml 中設定的預設密碼 R3dm0nd! 去連線客戶機的 Administrator 帳戶了。

安裝後你可以做的一些很酷的東西

儘管 iohyve uefi 功能尚未被正式支援,你依然可以使用內建的 iohyve 工具為 Windows 客戶機做一些很酷的關於動態檔案系統(ZFS)的一些事情,比如在安裝更新之前建立快照:
 
$ sudo iohyve snap win2k8@preupdate
Password:
Taking snapshot win2k8@preupdate$ iohyve snaplist
win2k8@preupdate

你也可以製作一個這個客戶機的獨立的拷貝,一旦你製作了這個映象,你就可以不必一遍又一遍的執行 Windows 的安裝過程了:

$ sudo iohyve clone win2k8 win2k8-deploy                                                                                             
Cloning win2k8 to win2k8-deploy$ iohyve list
Guest          VMM?  Running?  rcboot?  Description
win2k8        YES  NO        NO      Tue_Dec__8_10:30:51_MST_2015
win2k8-deploy  NO    NO        NO      Tue_Dec__8_11:34:38_MST_2015

英文原文:Running Windows under FreeBSD's bhyve

本文永久更新連結地址http://www.linuxidc.com/Linux/2015-12/126683.htm


IT145.com E-mail:sddin#qq.com