Unverified Commit fa40d30e authored by Kroese's avatar Kroese Committed by GitHub
Browse files

feat: Add Windows 2000 support (#1188)

parent de4bda71
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
ARG VERSION_ARG="latest"
FROM scratch AS build-amd64

COPY --from=qemux/qemu:7.08 / /
COPY --from=qemux/qemu:7.09 / /

ARG DEBCONF_NOWARNINGS="yes"
ARG DEBIAN_FRONTEND="noninteractive"
@@ -16,7 +16,8 @@ RUN set -eu && \
        dos2unix \
        cabextract \
        libxml2-utils \
        libarchive-tools && \
        libarchive-tools \
        netcat-openbsd && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

+22 −21
Original line number Diff line number Diff line
@@ -105,6 +105,7 @@ kubectl apply -f https://raw.githubusercontent.com/dockur/windows/refs/heads/mas
  | `7e`   | Windows 7 Enterprise      | 3.0 GB   |
  | `ve`   | Windows Vista Enterprise  | 3.0 GB   |
  | `xp`   | Windows XP Professional   | 0.6 GB   |
  | `2k`   | Windows 2000 Professional | 0.4 GB   | 
  ||||  
  | `2025` | Windows Server 2025       | 5.6 GB   |
  | `2022` | Windows Server 2022       | 4.7 GB   |
@@ -170,7 +171,7 @@ kubectl apply -f https://raw.githubusercontent.com/dockur/windows/refs/heads/mas

### How do I configure the username and password?

  By default, a user called `Docker` (with an empty password) is created during installation.
  By default, a user called `Docker` is created during installation and its password is `admin`.

  If you want to use different credentials, you can configure them in your compose file (only before installation):

@@ -258,7 +259,7 @@ kubectl apply -f https://raw.githubusercontent.com/dockur/windows/refs/heads/mas

  The web-viewer is mainly meant to be used during installation, as its picture quality is low, and it has no audio or clipboard for example.

  So for a better experience you can connect using any Microsoft Remote Desktop client to the IP of the container, using the username `Docker` and by leaving the password empty.
  So for a better experience you can connect using any Microsoft Remote Desktop client to the IP of the container, using the username `Docker` and password `admin`.

  There is a RDP client for [Android](https://play.google.com/store/apps/details?id=com.microsoft.rdc.androidx) available from the Play Store and one for [iOS](https://apps.apple.com/nl/app/microsoft-remote-desktop/id714464092?l=en-GB) in the Apple Store. For Linux you can use [FreeRDP](https://www.freerdp.com/) and on Windows just type `mstsc` in the search box.

+146 −88
Original line number Diff line number Diff line
@@ -89,6 +89,9 @@ parseVersion() {
    "xp64" | "xpx64" | "5x64" | "winxp64" | "winxpx64" | "windowsxp64" | "windowsxpx64" )
      VERSION="winxpx64"
      ;;
    "2k" | "2000" | "win2k" | "win2000" | "windows2k" | "windows2000" )
      VERSION="win2kx86"
      ;;
    "25" | "2025" | "win25" | "win2025" | "windows2025" | "windows 2025" )
      VERSION="win2025-eval"
      ;;
@@ -912,6 +915,11 @@ getLink1() {
      sum="8fac68e1e56c64ad9a2aa0ad464560282e67fa4f4dd51d09a66f4e548eb0f2d6"
      url="xp/professional/en_win_xp_pro_x64_vl.iso"
      ;;
    "win2kx86" )
      size=331701982
      sum="a93251b31f92316411bb48458a695d9051b13cdeba714c46f105012fdda45bf3"
      url="2000/5.00.2195.6717_x86fre_client-professional_retail_en-us.7z"
      ;;
  esac

  case "${ret,,}" in
@@ -1080,6 +1088,10 @@ getLink3() {
      sum="8fac68e1e56c64ad9a2aa0ad464560282e67fa4f4dd51d09a66f4e548eb0f2d6"
      url="windows-xp-all-sp-msdn-iso-files-en-de-ru-tr-x86-x64/en_win_xp_sp1_pro_x64_vl.iso"
      ;;
    "win2kx86" )
      size=386859008
      sum="e3816f6e80b66ff686ead03eeafffe9daf020a5e4717b8bd4736b7c51733ba22"
      url="MicrosoftWindows2000BuildCollection/5.00.2195.6717_x86fre_client-professional_retail_en-us-ZRMPFPP_EN.iso"
  esac

  case "${ret,,}" in
@@ -1216,26 +1228,32 @@ prepareInstall() {
  local driver="$4"
  local drivers="/tmp/drivers"

  rm -rf "$drivers"
  mkdir -p "$drivers"

  ETFS="[BOOT]/Boot-NoEmul.img"

  if [ ! -f "$dir/$ETFS" ] || [ ! -s "$dir/$ETFS" ]; then
    error "Failed to locate file \"$ETFS\" in $desc ISO image!" && return 1
  fi

  local arch target
  [ -d "$dir/AMD64" ] && arch="amd64" || arch="x86"
  [[ "${arch,,}" == "x86" ]] && target="$dir/I386" || target="$dir/AMD64"

  if [ ! -d "$target" ]; then
    error "Failed to locate directory \"$target\" in $desc ISO image!" && return 1
  fi

  if [[ "${driver,,}" == "xp" ]] || [[ "${driver,,}" == "2k3" ]]; then

    local msg="Adding drivers to image..."
    info "$msg" && html "$msg"

    rm -rf "$drivers"
    mkdir -p "$drivers"

    if ! bsdtar -xf /drivers.txz -C "$drivers"; then
      error "Failed to extract drivers!" && return 1
    fi

  local arch target
  [ -d "$dir/AMD64" ] && arch="amd64" || arch="x86"
  [[ "${arch,,}" == "x86" ]] && target="$dir/I386" || target="$dir/AMD64"

    if [ ! -f "$drivers/viostor/$driver/$arch/viostor.sys" ]; then
      error "Failed to locate required storage drivers!" && return 1
    fi
@@ -1289,8 +1307,13 @@ prepareInstall() {

    rm -rf "$drivers"

  fi

  local pid file setup
  setup=$(find "$target" -maxdepth 1 -type f -iname setupp.ini | head -n 1)

  if [ -n "$setup" ]; then

    pid=$(<"$setup")
    pid="${pid:(-4)}"
    pid="${pid:0:3}"
@@ -1299,6 +1322,10 @@ prepareInstall() {
      warn "this version of $desc requires a volume license key (VLK), it will ask for one during installation."
    fi

  fi

  mkdir -p "$dir/\$OEM\$"

  if ! addFolder "$dir"; then
    error "Failed to add OEM folder to image!" && return 1
  fi
@@ -1313,33 +1340,48 @@ prepareInstall() {
  XHEX=$(printf '%x\n' "$WIDTH")
  YHEX=$(printf '%x\n' "$HEIGHT")

  local username="Docker"
  local password="*"
  local username=""
  local password=""

  [ -n "$PASSWORD" ] && password="$PASSWORD"
  [ -n "$USERNAME" ] && username=$(echo "$USERNAME" | sed 's/[^[:alnum:]@!._-]//g')
  [ -z "$username" ] && username="Docker"

  [ -n "$PASSWORD" ] && password=$(echo "$PASSWORD" | sed 's/"//g')
  [ -z "$password" ] && password="admin"

  local ip="20.20.20.1"
  [ -n "${VM_NET_IP:-}" ] && ip="${VM_NET_IP%.*}.1"

  # These are not pirated keys, they come from the official MS documentation.
  if [[ "${driver,,}" == "xp" ]]; then
  case "${driver,,}" in
    "xp" )

      if [[ "${arch,,}" == "x86" ]]; then
        # Windows XP Professional x86 generic key (no activation, trial-only)
        [ -z "$KEY" ] && KEY="DR8GV-C8V6J-BYXHG-7PYJR-DB66Y"
      else
        # Windows XP Professional x64 generic key (no activation, trial-only)
        [ -z "$KEY" ] && KEY="B2RBK-7KPT9-4JP6X-QQFWM-PJD6G"
    fi
  else
      fi ;;

    "2k3" )

      if [[ "${arch,,}" == "x86" ]]; then
        # Windows Server 2003 Standard x86 generic key (no activation, trial-only)
        [ -z "$KEY" ] && KEY="QKDCQ-TP2JM-G4MDG-VR6F2-P9C48"
      else
        # Windows Server 2003 Standard x64 generic key (no activation, trial-only)
        [ -z "$KEY" ] && KEY="P4WJG-WK3W7-3HM8W-RWHCK-8JTRY"
    fi
  fi
      fi ;;

    "2k" )

      # Windows 2000 Professional x86 generic key
      KEY="G74HG-XXQTJ-RTX64-QKP3F-HKHXP" ;;

    * ) error "Unknown version: \"$driver\"" && return 1 ;;

  esac

  find "$target" -maxdepth 1 -type f -iname winnt.sif -exec rm {} \;

@@ -1378,7 +1420,7 @@ prepareInstall() {
          echo "    FullName=\"$username\""
          echo "    ComputerName=\"*\""
          echo "    OrgName=\"Windows for Docker\""
          echo "    ProductKey=$KEY"
          echo "    ProductID=$KEY"
          echo ""
          echo "[Identification]"
          echo "    JoinWorkgroup = WORKGROUP"
@@ -1437,12 +1479,14 @@ prepareInstall() {
          echo "[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced]"
          echo "\"HideFileExt\"=dword:00000000"
          echo ""
          echo "[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer]"
          echo "\"NoWelcomeScreen\"=\"1\""
          echo ""
          echo "[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon]"
          echo "\"AutoAdminLogon\"=\"1\""
          echo "\"DefaultUserName\"=\"$username\""
          echo "\"DefaultPassword\"=\"$password\""
          echo "\"DefaultDomainName\"=\"Dockur\""
          echo "\"AltDefaultUserName\"=\"$username\""
          echo "\"AltDefaultDomainName\"=\"Dockur\""
          echo "\"AutoAdminLogon\"=\"1\""
          echo ""
          echo "[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Video\{23A77BF7-ED96-40EC-AF06-9B1F4867732A}\0000]"
          echo "\"DefaultSettings.BitsPerPel\"=dword:00000020"
@@ -1513,11 +1557,13 @@ prepareInstall() {
          echo ""
          echo "Call Domain.MoveHere(LocalAdminADsPath, \"$username\")"
          echo ""
          echo "Set oFSO = CreateObject(\"Scripting.FileSystemObject\")"
          echo "Set oHosts = oFSO.GetFile(\"C:\Windows\System32\drivers\etc\hosts\")"
          echo "Set fileAPPEND = oFSO.OpenTextFile(\"C:\Windows\System32\drivers\etc\hosts\", 8, true)"
          echo "fileAPPEND.Write(\"$ip      host.lan\")"
          echo "fileAPPEND.Close()"
          echo "With (CreateObject(\"Scripting.FileSystemObject\"))"
          echo "  SysRoot = WshShell.ExpandEnvironmentStrings(\"%SystemRoot%\")"
          echo "  Set oFile = .OpenTextFile(SysRoot & \"\system32\drivers\etc\hosts\", 8, true)"
          echo "  oFile.Write(\"$ip      host.lan\")"
          echo "  oFile.Close()"
          echo "  Set oFile = Nothing"
          echo "End With"
          echo ""
  } | unix2dos > "$dir/\$OEM\$/admin.vbs"

@@ -1614,7 +1660,9 @@ setMachine() {
    "win9"* )
      ETFS="[BOOT]/Boot-1.44M.img" ;;
    "win2k"* )
      ETFS="[BOOT]/Boot-NoEmul.img" ;;
      if ! prepareInstall "$iso" "$dir" "$desc" "2k"; then
        error "Failed to prepare $desc ISO!" && return 1
      fi ;;
    "winxp"* )
      if ! prepareInstall "$iso" "$dir" "$desc" "xp"; then
        error "Failed to prepare $desc ISO!" && return 1
@@ -1626,10 +1674,20 @@ setMachine() {
  esac

  case "${id,,}" in
    "win9"* | "win2k"* )
    "win9"* )
      USB="no"
      VGA="cirrus"
      DISK_TYPE="auto"
      ADAPTER="rtl8139"
      MACHINE="pc-i440fx-2.4"
      BOOT_MODE="windows_legacy" ;;
    "win2k"* )
      VGA="cirrus"
      MACHINE="pc"
      USB="pci-ohci"
      DISK_TYPE="auto"
      ADAPTER="rtl8139"
      BOOT_MODE="windows_legacy" ;;
    "winxp"* | "win2003"* )
      DISK_TYPE="blk"
      BOOT_MODE="windows_legacy" ;;
+70 −13
Original line number Diff line number Diff line
@@ -128,6 +128,8 @@ finishInstall() {

  rm -f "$STORAGE/windows.old"
  rm -f "$STORAGE/windows.vga"
  rm -f "$STORAGE/windows.net"
  rm -f "$STORAGE/windows.usb"
  rm -f "$STORAGE/windows.args"
  rm -f "$STORAGE/windows.base"
  rm -f "$STORAGE/windows.boot"
@@ -170,10 +172,22 @@ finishInstall() {
    echo "$ARGS" > "$STORAGE/windows.args"
  fi

  if [ -n "${VGA:-}" ] && [[ "${VGA:-}" != "virtio"* ]]; then
    echo "$VGA" > "$STORAGE/windows.vga"
  fi

  if [ -n "${USB:-}" ] && [[ "${USB:-}" != "qemu-xhci"* ]]; then
    echo "$USB" > "$STORAGE/windows.usb"
  fi

  if [ -n "${DISK_TYPE:-}" ] && [[ "${DISK_TYPE:-}" != "scsi" ]]; then
    echo "$DISK_TYPE" > "$STORAGE/windows.type"
  fi

  if [ -n "${ADAPTER:-}" ] && [[ "${ADAPTER:-}" != "virtio-net-pci" ]]; then
    echo "$ADAPTER" > "$STORAGE/windows.net"
  fi

  rm -rf "$TMP"
  return 0
}
@@ -185,6 +199,7 @@ abortInstall() {
  local efi

  [[ "${iso,,}" == *".esd" ]] && exit 60
  [[ "${UNPACK:-}" == [Yy1]* ]] && exit 60

  efi=$(find "$dir" -maxdepth 1 -type d -iname efi | head -n 1)

@@ -336,7 +351,7 @@ extractImage() {
  local dir="$2"
  local version="$3"
  local desc="local ISO"
  local size size_gb space space_gb
  local file size size_gb space space_gb

  if [ -z "$CUSTOM" ]; then
    desc="downloaded ISO"
@@ -375,8 +390,27 @@ extractImage() {
    error "Failed to extract ISO file: $iso" && return 1
  fi

  if [[ "${UNPACK:-}" != [Yy1]* ]]; then

    LABEL=$(isoinfo -d -i "$iso" | sed -n 's/Volume id: //p')

  else

    file=$(find "$dir" -maxdepth 1 -type f -iname "*.iso" | head -n 1)

    if [ -z "$file" ]; then
      error "Failed to find any .iso file in archive!" && return 1
    fi

    if ! 7z x "$file" -o"$dir" > /dev/null; then
      error "Failed to extract archive!" && return 1
    fi

    LABEL=$(isoinfo -d -i "$file" | sed -n 's/Volume id: //p')
    rm -f "$file"

  fi

  return 0
}

@@ -689,16 +723,18 @@ updateXML() {
    sed -i "s/<Username>Docker<\/Username>/<Username>$user<\/Username>/g" "$asset"
  fi

  if [ -n "$PASSWORD" ]; then
    pass=$(printf '%s' "${PASSWORD}Password" | iconv -f utf-8 -t utf-16le | base64 -w 0)
    admin=$(printf '%s' "${PASSWORD}AdministratorPassword" | iconv -f utf-8 -t utf-16le | base64 -w 0)
  [ -n "$PASSWORD" ] && pass="$PASSWORD"
  [ -z "$pass" ] && pass="admin"

  pw=$(printf '%s' "${pass}Password" | iconv -f utf-8 -t utf-16le | base64 -w 0)
  admin=$(printf '%s' "${pass}AdministratorPassword" | iconv -f utf-8 -t utf-16le | base64 -w 0)

  sed -i "s/<Value>password<\/Value>/<Value>$admin<\/Value>/g" "$asset"
  sed -i "s/<PlainText>true<\/PlainText>/<PlainText>false<\/PlainText>/g" "$asset"
    sed -z "s/<Password>...........<Value \/>/<Password>\n          <Value>$pass<\/Value>/g" -i "$asset"
    sed -z "s/<Password>...............<Value \/>/<Password>\n              <Value>$pass<\/Value>/g" -i "$asset"
  sed -z "s/<Password>...........<Value \/>/<Password>\n          <Value>$pw<\/Value>/g" -i "$asset"
  sed -z "s/<Password>...............<Value \/>/<Password>\n              <Value>$pw<\/Value>/g" -i "$asset"
  sed -z "s/<AdministratorPassword>...........<Value \/>/<AdministratorPassword>\n          <Value>$admin<\/Value>/g" -i "$asset"
  sed -z "s/<AdministratorPassword>...............<Value \/>/<AdministratorPassword>\n              <Value>$admin<\/Value>/g" -i "$asset"
  fi

  if [ -n "$EDITION" ]; then
    [[ "${EDITION^^}" == "CORE" ]] && EDITION="STANDARDCORE"
@@ -1034,6 +1070,27 @@ bootWindows() {
    ARGUMENTS="$ARGS ${ARGUMENTS:-}"
  fi

  if [ -s "$STORAGE/windows.vga" ] && [ -f "$STORAGE/windows.vga" ]; then
    if [ -z "${VGA:-}" ]; then
      VGA=$(<"$STORAGE/windows.vga")
      VGA="${VGA//[![:print:]]/}"
    fi
  fi

  if [ -s "$STORAGE/windows.usb" ] && [ -f "$STORAGE/windows.usb" ]; then
    if [ -z "${USB:-}" ]; then
      USB=$(<"$STORAGE/windows.usb")
      USB="${USB//[![:print:]]/}"
    fi
  fi

  if [ -s "$STORAGE/windows.net" ] && [ -f "$STORAGE/windows.net" ]; then
    if [ -z "${ADAPTER:-}" ]; then
      ADAPTER=$(<"$STORAGE/windows.net")
      ADAPTER="${ADAPTER//[![:print:]]/}"
    fi
  fi

  if [ -s "$STORAGE/windows.type" ] && [ -f "$STORAGE/windows.type" ]; then
    if [ -z "${DISK_TYPE:-}" ]; then
      DISK_TYPE=$(<"$STORAGE/windows.type")
+15 −0
Original line number Diff line number Diff line
@@ -472,6 +472,18 @@ getESD() {
  return 0
}

isCompressed() {

  local file="$1"

  case "${file,,}" in
    *".7z" | *".zip" | *".rar" | *".lzma" | *".bz" | *".bz2" )
      return 0 ;;
  esac

  return 1
}

verifyFile() {

  local iso="$1"
@@ -560,6 +572,7 @@ downloadFile() {
      error "Invalid download link: $url (is only $total_gb ?). Please report this at $SUPPORT/issues." && return 1
    fi
    verifyFile "$iso" "$size" "$total" "$sum" || return 1
    isCompressed "$url" && UNPACK="Y"
    html "Download finished successfully..." && return 0
  fi

@@ -584,12 +597,14 @@ downloadImage() {
  local msg="Will retry after $delay seconds..."

  if [[ "${version,,}" == "http"* ]]; then

    base=$(basename "$iso")
    desc=$(fromFile "$base")
    downloadFile "$iso" "$version" "" "" "" "$desc" && return 0
    info "$msg" && html "$msg" && sleep "$delay"
    downloadFile "$iso" "$version" "" "" "" "$desc" && return 0
    rm -f "$iso"

    return 1
  fi