Guest User

Untitled

a guest
Aug 27th, 2026
107
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.47 KB | None | 0 0
  1. #!/usr/bin/env bash
  2. #
  3. # fix-openvas-scan-0.sh
  4. #
  5. # Fixes Kali/OpenVAS scans failing at 0% with ospd-openvas crashing on:
  6. # TypeError: cannot pickle '_thread.RLock' object
  7. #
  8. # Root cause:
  9. # Python 3.14 changed the default multiprocessing start method away from
  10. # 'fork'. ospd-openvas keeps a Redis connection pool (containing an
  11. # unpicklable _thread.RLock) on the daemon object, so starting a queued
  12. # scan process now fails while pickling and the scanner dies at 0%.
  13. #
  14. # The official fix (ospd-openvas v22.10.5, commit a99fe321,
  15. # https://github.com/greenbone/ospd-openvas/releases) adds one line to
  16. # ospd/ospd.py:
  17. #
  18. # multiprocessing.set_start_method('fork', force=True)
  19. #
  20. # This script applies exactly that fix to the ospd package installed on the
  21. # system, backs up the original file, verifies the result and restarts the
  22. # scanner service.
  23. #
  24. # Usage: sudo ./fix-openvas-scan-0.sh
  25. #
  26. set -euo pipefail
  27.  
  28. RED=$'\e[31m'; GREEN=$'\e[32m'; YELLOW=$'\e[33m'; BOLD=$'\e[1m'; RESET=$'\e[0m'
  29. info() { printf '%s[*]%s %s\n' "$BOLD" "$RESET" "$*"; }
  30. ok() { printf '%s[+]%s %s\n' "$GREEN" "$RESET" "$*"; }
  31. warn() { printf '%s[!]%s %s\n' "$YELLOW" "$RESET" "$*"; }
  32. die() { printf '%s[x]%s %s\n' "$RED" "$RESET" "$*" >&2; exit 1; }
  33.  
  34. # --- 0. Parse arguments -----------------------------------------------------
  35. # "check" -> only report whether the fix is already applied, then exit.
  36. CHECK_ONLY=0
  37. if [[ "${1:-}" == "check" ]]; then
  38. CHECK_ONLY=1
  39. fi
  40.  
  41. # --- 1. Must run as root ----------------------------------------------------
  42. # Check-only mode is read-only, so it does not require root.
  43. if [[ "${CHECK_ONLY}" -eq 0 && ${EUID:-$(id -u)} -ne 0 ]]; then
  44. die "Please run as root: sudo $0"
  45. fi
  46.  
  47. # --- 2. Locate the ospd package -------------------------------------------
  48. info "Locating the installed 'ospd' Python package..."
  49. OSPD_PY=""
  50. if command -v python3 >/dev/null 2>&1; then
  51. OSPD_PY=$(python3 - <<'PY' 2>/dev/null || true
  52. import importlib.util
  53. spec = importlib.util.find_spec("ospd.ospd")
  54. print(spec.origin if spec and spec.origin else "")
  55. PY
  56. )
  57. fi
  58.  
  59. # Fall back to common Debian/Kali locations if import lookup failed.
  60. if [[ -z "${OSPD_PY}" || ! -f "${OSPD_PY}" ]]; then
  61. for cand in \
  62. /usr/lib/python3/dist-packages/ospd/ospd.py \
  63. /usr/local/lib/python3*/dist-packages/ospd/ospd.py \
  64. /usr/lib/python3*/site-packages/ospd/ospd.py; do
  65. if [[ -f "$cand" ]]; then OSPD_PY="$cand"; break; fi
  66. done
  67. fi
  68.  
  69. [[ -n "${OSPD_PY}" && -f "${OSPD_PY}" ]] \
  70. || die "Could not find ospd/ospd.py. Is ospd-openvas installed?"
  71. ok "Found: ${OSPD_PY}"
  72.  
  73. # --- 3. Check whether the fix is already present ----------------------------
  74. if grep -Eq "set_start_method\(\s*['\"]fork['\"]" "${OSPD_PY}"; then
  75. ok "Fix already present in ${OSPD_PY}."
  76. [[ "${CHECK_ONLY}" -eq 1 ]] && exit 0
  77. PATCHED_ALREADY=1
  78. else
  79. if [[ "${CHECK_ONLY}" -eq 1 ]]; then
  80. warn "Fix NOT applied in ${OSPD_PY}. Run without 'check' as root to apply it."
  81. exit 1
  82. fi
  83. PATCHED_ALREADY=0
  84.  
  85. # --- 4. Back up the original --------------------------------------------
  86. BACKUP="${OSPD_PY}.bak.$(date +%Y%m%d%H%M%S)"
  87. cp -a "${OSPD_PY}" "${BACKUP}"
  88. ok "Backup created: ${BACKUP}"
  89.  
  90. # --- 5. Apply the patch with Python (safe, idempotent) ------------------
  91. info "Applying multiprocessing start-method fix..."
  92. python3 - "${OSPD_PY}" <<'PY'
  93. import io, re, sys
  94.  
  95. path = sys.argv[1]
  96. with io.open(path, "r", encoding="utf-8") as f:
  97. src = f.read()
  98.  
  99. fix_line = "multiprocessing.set_start_method('fork', force=True)\n"
  100.  
  101. # Ensure the multiprocessing module is imported.
  102. if not re.search(r"^\s*import\s+multiprocessing\b", src, re.MULTILINE):
  103. # Insert the import after the last top-level import line.
  104. lines = src.splitlines(keepends=True)
  105. last_import = 0
  106. for i, line in enumerate(lines):
  107. if re.match(r"^\s*(import|from)\s+\S", line):
  108. last_import = i + 1
  109. lines.insert(last_import, "import multiprocessing\n")
  110. src = "".join(lines)
  111.  
  112. # Insert the fix right after the module logger definition (matches upstream),
  113. # or after the imports as a fallback.
  114. if "set_start_method('fork'" not in src and 'set_start_method("fork"' not in src:
  115. anchor = re.search(r"^logger\s*=\s*logging\.getLogger\(__name__\).*$",
  116. src, re.MULTILINE)
  117. if anchor:
  118. idx = anchor.end()
  119. src = src[:idx] + "\n\n" + fix_line + src[idx:]
  120. else:
  121. lines = src.splitlines(keepends=True)
  122. last_import = 0
  123. for i, line in enumerate(lines):
  124. if re.match(r"^\s*(import|from)\s+\S", line):
  125. last_import = i + 1
  126. lines.insert(last_import, "\n" + fix_line)
  127. src = "".join(lines)
  128.  
  129. with io.open(path, "w", encoding="utf-8") as f:
  130. f.write(src)
  131.  
  132. print("patched")
  133. PY
  134.  
  135. # --- 6. Verify the patched file still compiles --------------------------
  136. info "Verifying Python syntax of the patched file..."
  137. if ! python3 -m py_compile "${OSPD_PY}"; then
  138. warn "Syntax check failed. Restoring original from backup."
  139. cp -a "${BACKUP}" "${OSPD_PY}"
  140. die "Patch reverted. No changes made to a working file."
  141. fi
  142. grep -Eq "set_start_method\(\s*['\"]fork['\"]" "${OSPD_PY}" \
  143. || { cp -a "${BACKUP}" "${OSPD_PY}"; die "Fix not detected after patch; reverted."; }
  144. ok "Patch applied and verified."
  145. fi
  146.  
  147. # --- 7. Restart the scanner service ----------------------------------------
  148. SERVICE=""
  149. for svc in ospd-openvas gvmd; do
  150. if systemctl list-unit-files 2>/dev/null | grep -q "^${svc}\.service"; then
  151. SERVICE="ospd-openvas"; break
  152. fi
  153. done
  154.  
  155. if [[ -n "${SERVICE}" ]] && command -v systemctl >/dev/null 2>&1; then
  156. info "Restarting ospd-openvas service..."
  157. systemctl restart ospd-openvas.service || warn "Could not restart ospd-openvas via systemctl."
  158. sleep 2
  159. if systemctl is-active --quiet ospd-openvas.service; then
  160. ok "ospd-openvas is running."
  161. else
  162. warn "ospd-openvas is not active. Check: journalctl -u ospd-openvas -e"
  163. fi
  164. else
  165. warn "systemd ospd-openvas.service not found. Restart the scanner manually."
  166. fi
  167.  
  168. echo
  169. if [[ "${PATCHED_ALREADY}" -eq 1 ]]; then
  170. ok "System already patched. If scans still fail, ensure the feed sync is complete."
  171. else
  172. ok "Done. Start a scan again — it should now progress past 0%."
  173. fi
  174. info "Verify with: gvm-check-setup and journalctl -u ospd-openvas -e"
  175.  
Advertisement
Add Comment
Please, Sign In to add comment