smoke-firstboot.sh 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. #!/usr/bin/env bash
  2. #
  3. # smoke-firstboot.sh — verify the first-boot per-instance credential script.
  4. #
  5. # Installs the released x-ui binary into a container WITHOUT a database, runs
  6. # x-ui-firstboot.sh, and asserts:
  7. # * fresh random credentials are generated (no admin/admin)
  8. # * /etc/x-ui/credentials.txt (600) and /etc/motd are written
  9. # * the sentinel is created and a second run is a no-op (creds unchanged)
  10. #
  11. # Requires Docker and network access. Usage: bash deploy/test/smoke-firstboot.sh
  12. set -euo pipefail
  13. REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
  14. IMAGE="${SMOKE_IMAGE:-ubuntu:24.04}"
  15. if ! command -v docker > /dev/null 2>&1; then
  16. echo "ERROR: docker is required for this smoke test." >&2
  17. exit 1
  18. fi
  19. echo "== first-boot credential smoke test (image: $IMAGE) =="
  20. docker run --rm \
  21. -v "${REPO_ROOT}/deploy/firstboot/x-ui-firstboot.sh:/root/x-ui-firstboot.sh:ro" \
  22. -e DEBIAN_FRONTEND=noninteractive \
  23. "$IMAGE" bash -euo pipefail -c '
  24. apt-get update -qq
  25. apt-get install -y -qq curl tar openssl ca-certificates jq > /dev/null
  26. echo "--- installing released x-ui binary (no DB, no systemd) ---"
  27. REPO=MHSanaei/3x-ui
  28. ARCH=$(dpkg --print-architecture) # amd64 | arm64
  29. echo "container arch: $ARCH"
  30. VER=$(curl -fsSL "https://api.github.com/repos/${REPO}/releases/latest" | jq -r .tag_name)
  31. [ -n "$VER" ] && [ "$VER" != "null" ] || { echo "FAIL: cannot resolve version"; exit 1; }
  32. tmp=$(mktemp -d)
  33. curl -fL4 -o "${tmp}/x.tar.gz" \
  34. "https://github.com/${REPO}/releases/download/${VER}/x-ui-linux-${ARCH}.tar.gz"
  35. tar -xzf "${tmp}/x.tar.gz" -C /usr/local/
  36. chmod +x /usr/local/x-ui/x-ui
  37. install -m 755 /root/x-ui-firstboot.sh /usr/local/x-ui/x-ui-firstboot.sh
  38. # Guarantee a clean slate (the image must never ship a DB).
  39. rm -f /etc/x-ui/x-ui.db /etc/x-ui/.firstboot-done
  40. echo "--- run 1: generate per-instance credentials ---"
  41. /usr/local/x-ui/x-ui-firstboot.sh
  42. test -f /etc/x-ui/.firstboot-done || { echo "FAIL: sentinel not created"; exit 1; }
  43. test -f /etc/x-ui/credentials.txt || { echo "FAIL: credentials.txt missing"; exit 1; }
  44. perms=$(stat -c %a /etc/x-ui/credentials.txt)
  45. [ "$perms" = "600" ] || { echo "FAIL: credentials.txt perms=$perms (want 600)"; exit 1; }
  46. grep -q "3x-ui" /etc/motd || { echo "FAIL: motd not written"; exit 1; }
  47. # shellcheck disable=SC1090
  48. . /etc/x-ui/credentials.txt
  49. [ -n "${XUI_USERNAME:-}" ] && [ "$XUI_USERNAME" != "admin" ] \
  50. || { echo "FAIL: username missing or still admin"; exit 1; }
  51. first_user="$XUI_USERNAME"
  52. /usr/local/x-ui/x-ui setting -show | grep -q "hasDefaultCredential: false" \
  53. || { echo "FAIL: hasDefaultCredential is not false"; exit 1; }
  54. echo "--- run 2: must be a no-op (sentinel honored) ---"
  55. /usr/local/x-ui/x-ui-firstboot.sh
  56. # shellcheck disable=SC1090
  57. . /etc/x-ui/credentials.txt
  58. [ "$XUI_USERNAME" = "$first_user" ] \
  59. || { echo "FAIL: credentials changed on re-run"; exit 1; }
  60. echo "SMOKE_PASS: firstboot user=$first_user (stable across re-run)"
  61. '
  62. echo "== first-boot smoke test PASSED =="