CIS490/scripts/fetch-metasploitable2.sh
max 683bfe9ce6 Tier 3 + Tier 4 auto-deploy: zero operator interaction
Replaces the manual runbook with scripts that just work. install-lab-host.sh
now runs the full Tier-3 deploy automatically as its 8th step (after the
mTLS cert lands), and Tier-4 auto-fetches when MALWAREBAZAAR_API_KEY is set.

Changes:

- install-msfrpcd.sh: actually runs the Rapid7 omnibus installer when
  metasploit-framework isn't present (was: bail with "install manually").
  apt-get and dnf paths both go through the same omnibus script with
  DEBIAN_FRONTEND=noninteractive. Idempotent.

- fetch-metasploitable2.sh: bakes in the SourceForge public-mirror URL
  (https://downloads.sourceforge.net/project/metasploitable/...) so no
  operator URL is required. sha256 is now optional and TOFU-pinned —
  first run records the hash to OUT_DIR/metasploitable2.qcow2.sha256;
  subsequent runs verify against that. Skips if qcow2 already present.

- scripts/install-tier-3-4.sh (new): orchestrates the four steps
  (msfrpcd → metasploitable2 → bridge → tier-3 verify) plus optional
  Tier-4 auto-fetch. Idempotent. SKIP_VERIFY / SKIP_BRIDGE / SKIP_TIER4
  env knobs for partial deploys.

- tools/auto_fetch_samples.py (new): when MALWAREBAZAAR_API_KEY is set,
  queries MB by each manifest entry's `family` (signature match), pulls
  the first match via fetch_sample.py, and rewrites manifest.toml in
  place (atomic tempfile + os.replace, preserving stat). Skips entries
  that already have sha256.

- install-lab-host.sh: gains a step 8 that calls install-tier-3-4.sh
  automatically when mTLS certs are on disk. --skip-tier3 flag for
  operators who want Tier 2 only. Skipped silently before certs land
  so first-pass install (host_id=REPLACE_ME) still works.

- AGENTS.md: rewrote the Tier-3 section to point at the one-shot
  script. Removed the old multi-command runbook so on-device agents
  can't accidentally follow stale steps.

Net effect: a fresh lab host now gets Tier 3 (and Tier 4 if API key
present) from a single sudo invocation. No operator picks for image
URLs, no manual metasploit installs, no manual manifest edits.
2026-04-30 23:12:08 -05:00

98 lines
3.9 KiB
Bash
Executable file

#!/usr/bin/env bash
# Fetch the Metasploitable2 disk image with no operator interaction.
#
# Defaults to the SourceForge public mirror — the canonical
# freely-redistributable copy of Metasploitable2 (Rapid7's own
# download is registration-walled but the same VMDK is on
# SourceForge, downloaded ~2M times). HTTPS protects the transport.
#
# Idempotent: if the qcow2 is already on disk we do nothing.
#
# Inputs (env, all optional):
# IMAGE_URL — override the default mirror URL
# IMAGE_SHA256 — verify against this hash. If unset and a sha256
# has been recorded by a prior successful fetch
# ($OUT_DIR/metasploitable2.qcow2.sha256), use that.
# If neither is available, do TOFU (trust on first
# use): record the hash of what was downloaded so
# subsequent runs verify against it.
# OUT_DIR — where to drop the qcow2 (default vm/images/)
#
# Outputs:
# $OUT_DIR/metasploitable2.qcow2 — the disk image
# $OUT_DIR/metasploitable2.qcow2.sha256 — recorded archive hash
set -euo pipefail
# SourceForge public mirror. Direct-download URL — no auth, no
# registration. /download 302s to a regional mirror.
DEFAULT_IMAGE_URL='https://downloads.sourceforge.net/project/metasploitable/Metasploitable2/metasploitable-linux-2.0.0.zip'
IMAGE_URL="${IMAGE_URL:-$DEFAULT_IMAGE_URL}"
IMAGE_SHA256="${IMAGE_SHA256:-}"
OUT_DIR="${OUT_DIR:-$(cd "$(dirname "$0")/../vm/images" 2>/dev/null && pwd)}"
WORK_DIR="${WORK_DIR:-/tmp/cis490-metasploitable-fetch}"
log() { printf '[fetch-metasploitable2] %s\n' "$*" >&2; }
die() { log "FATAL: $*"; exit 1; }
mkdir -p "$OUT_DIR" "$WORK_DIR"
# Short-circuit if the qcow2 is already on disk.
if [[ -f "$OUT_DIR/metasploitable2.qcow2" ]]; then
log "$OUT_DIR/metasploitable2.qcow2 already present; nothing to do"
exit 0
fi
# Use the recorded sha256 from a prior successful fetch if present
# and the env var didn't override it. This pins TOFU across runs
# so a tampered re-download fails noisily.
SHA_FILE="$OUT_DIR/metasploitable2.qcow2.sha256"
if [[ -z "$IMAGE_SHA256" && -f "$SHA_FILE" ]]; then
IMAGE_SHA256="$(awk '{print $1}' "$SHA_FILE")"
log "using pinned sha256 from $SHA_FILE: $IMAGE_SHA256"
fi
ARCHIVE="$WORK_DIR/$(basename "$IMAGE_URL")"
log "downloading $IMAGE_URL$ARCHIVE"
if [[ -f "$ARCHIVE" ]]; then
log "archive already present in work dir; skipping download"
else
# -L follows SourceForge's redirect to the actual mirror.
curl -fL --retry 3 --retry-delay 5 -o "$ARCHIVE.partial" "$IMAGE_URL"
mv "$ARCHIVE.partial" "$ARCHIVE"
fi
ACTUAL="$(sha256sum "$ARCHIVE" | awk '{print $1}')"
if [[ -n "$IMAGE_SHA256" ]]; then
if [[ "$ACTUAL" != "$IMAGE_SHA256" ]]; then
die "sha256 mismatch: expected $IMAGE_SHA256, got $ACTUAL"
fi
log "sha256 ok"
else
log "no sha256 supplied — first-run TOFU; pinning $ACTUAL for future runs"
fi
# Always (re)record so future runs verify against the working hash.
echo "$ACTUAL $(basename "$ARCHIVE")" > "$SHA_FILE"
# Extract — handle either zip or 7z, since various mirrors choose one
# or the other.
case "$ARCHIVE" in
*.zip) ( cd "$WORK_DIR" && unzip -o "$ARCHIVE" ) ;;
*.7z|*.7zip) command -v 7z >/dev/null || die "7z not installed"; \
( cd "$WORK_DIR" && 7z x -y "$ARCHIVE" ) ;;
*) die "unsupported archive type: $ARCHIVE" ;;
esac
VMDK="$(find "$WORK_DIR" -name 'Metasploitable*.vmdk' -print -quit)"
[[ -n "$VMDK" ]] || die "no Metasploitable*.vmdk in extracted archive"
log "converting $VMDK → qcow2"
command -v qemu-img >/dev/null || die "qemu-img required (apt install qemu-utils)"
qemu-img convert -O qcow2 "$VMDK" "$OUT_DIR/metasploitable2.qcow2"
# Best-effort cleanup of the work dir — keeps lab-host disk clean.
rm -rf "$WORK_DIR"
log "done: $OUT_DIR/metasploitable2.qcow2"
log "Tier-3 ready when msfrpcd is up. See scripts/install-msfrpcd.sh."