From 5930fd7ab5935116f1bf9562b75bfdde76afd383 Mon Sep 17 00:00:00 2001 From: Tero Date: Sat, 5 Sep 2026 12:14:36 +0300 Subject: [PATCH] first commit --- .gitattributes | 3 + Dockerfile | 9 ++ README.md | 36 ++++++ build.sh | 31 +++++ .../normal/010-enable-console.hook.chroot | 4 + .../etc/systemd/system/ep-eraser.service | 19 ++++ .../includes.chroot/usr/local/sbin/ep-eraser | 107 ++++++++++++++++++ config/package-lists/ep-eraser.list.chroot | 8 ++ 8 files changed, 217 insertions(+) create mode 100644 .gitattributes create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 build.sh create mode 100644 config/hooks/normal/010-enable-console.hook.chroot create mode 100644 config/includes.chroot/etc/systemd/system/ep-eraser.service create mode 100644 config/includes.chroot/usr/local/sbin/ep-eraser create mode 100644 config/package-lists/ep-eraser.list.chroot diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..7521e77 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +*.sh text eol=lf +*.hook.chroot text eol=lf +Dockerfile text eol=lf \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..9cc7aed --- /dev/null +++ b/Dockerfile @@ -0,0 +1,9 @@ +FROM debian:trixie + +RUN apt-get update \ + && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ + live-build ca-certificates debootstrap xorriso squashfs-tools dosfstools \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /work +ENTRYPOINT ["/bin/sh", "-c", "tr -d '\\r' < /work/build.sh | EP_ERASER_ROOT=/work /bin/sh"] \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..6b10ddc --- /dev/null +++ b/README.md @@ -0,0 +1,36 @@ +# EP Eraser + +EP Eraser is a small Debian Live image for securely retiring SATA/NVMe SSDs and rotational HDDs. It boots into a text console inspired by DBAN, but uses device-specific erase methods instead of overwriting every disk indiscriminately. + +## Safety model + +- The tool requires an exact device name and a typed `ERASE` confirmation. +- It refuses mounted devices, the live boot device, read-only devices, and devices with mounted descendants. +- SSDs use `blkdiscard --secure` where supported, with NVMe sanitize as an option. +- HDDs use a three-pass `shred` overwrite followed by a device-state/status check. +- Every operation is logged to `/var/log/ep-eraser.log` and a summary is written to the target's parent device metadata only when supported. + +No software can guarantee recovery resistance for every controller, reserved area, remapped sector, or damaged drive. For high-assurance disposal, combine this image with the drive vendor's sanitize command or physical destruction according to your organization's policy. + +## Build + +Build from Debian/Ubuntu, WSL, or Docker. The builder must run as root because `live-build` creates a chroot. + +```sh +sudo ./build.sh +``` + +The output is `build/ep-eraser-amd64.hybrid.iso`. Write it to a USB drive with a tool such as Rufus or `dd`. Verify the output checksum before deployment. + +To build without installing packages on the host, use Docker from Linux or WSL: + +```sh +docker build -t ep-eraser-builder . +docker run --rm -v "$PWD:/work" ep-eraser-builder +``` + +## Use + +Boot the target machine from the image, inspect the disk list, and choose the exact device path. The program detects rotational media and presents the appropriate operation. Reboot after completion; do not remove the USB device until the result is shown. + +This project intentionally does not include an automatic wipe-all mode. \ No newline at end of file diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..2af98a5 --- /dev/null +++ b/build.sh @@ -0,0 +1,31 @@ +#!/bin/sh +set -eu + +if [ "$(id -u)" -ne 0 ]; then + echo "Run this builder as root (or use the Dockerfile)." >&2 + exit 1 +fi + +ROOT=${EP_ERASER_ROOT:-$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)} +cd "$ROOT" +rm -rf build +mkdir -p build + +lb config \ + --distribution trixie \ + --architectures amd64 \ + --binary-images iso-hybrid \ + --debian-installer false \ + --archive-areas "main contrib non-free-firmware" \ + --apt-recommends false \ + --bootappend-live "boot=live components username=eraser hostname=ep-eraser" \ + --iso-application "EP Eraser" \ + --iso-publisher "EP Eraser Project" \ + --iso-volume "EP_ERASER" \ + --image-name "ep-eraser" \ + --source false + +lb build +mv ./*.hybrid.iso build/ep-eraser-amd64.hybrid.iso +sha256sum build/ep-eraser-amd64.hybrid.iso > build/ep-eraser-amd64.hybrid.iso.sha256 +echo "Built build/ep-eraser-amd64.hybrid.iso" \ No newline at end of file diff --git a/config/hooks/normal/010-enable-console.hook.chroot b/config/hooks/normal/010-enable-console.hook.chroot new file mode 100644 index 0000000..1eaefe0 --- /dev/null +++ b/config/hooks/normal/010-enable-console.hook.chroot @@ -0,0 +1,4 @@ +#!/bin/sh +set -eu +chmod 0755 /usr/local/sbin/ep-eraser +systemctl enable ep-eraser.service \ No newline at end of file diff --git a/config/includes.chroot/etc/systemd/system/ep-eraser.service b/config/includes.chroot/etc/systemd/system/ep-eraser.service new file mode 100644 index 0000000..9619b9d --- /dev/null +++ b/config/includes.chroot/etc/systemd/system/ep-eraser.service @@ -0,0 +1,19 @@ +[Unit] +Description=EP Eraser console +After=local-fs.target systemd-udev-settle.service +Wants=systemd-udev-settle.service + +[Service] +Type=simple +ExecStart=/usr/local/sbin/ep-eraser +StandardInput=tty +StandardOutput=tty +StandardError=tty +TTYPath=/dev/tty1 +TTYReset=yes +TTYVHangup=yes +TTYVTDisallocate=yes +Restart=no + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/config/includes.chroot/usr/local/sbin/ep-eraser b/config/includes.chroot/usr/local/sbin/ep-eraser new file mode 100644 index 0000000..7dbf21a --- /dev/null +++ b/config/includes.chroot/usr/local/sbin/ep-eraser @@ -0,0 +1,107 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +LOG=/var/log/ep-eraser.log +exec > >(tee -a "$LOG") 2>&1 + +die() { printf '\nERROR: %s\n' "$*" >&2; read -r -p 'Press Enter to return to the menu...' _; } + +mounted() { + findmnt -rn -S "$1" >/dev/null 2>&1 && return 0 + lsblk -nrpo NAME,MOUNTPOINTS "$1" | awk 'NF > 1 && $2 != "" { found=1 } END { exit !found }' +} + +is_live_device() { + local live parent + live=$(findmnt -rn -o SOURCE /run/live/medium 2>/dev/null || true) + [ -n "$live" ] || return 1 + parent=$(lsblk -dnro PKNAME "$live" 2>/dev/null || true) + [ -n "$parent" ] && [ "$1" = "/dev/$parent" ] +} + +disk_info() { + lsblk -dnpo NAME,TYPE,SIZE,MODEL,SERIAL,TRAN | awk '$2 == "disk" { print }' +} + +choose_disk() { + mapfile -t disks < <(lsblk -dnpo NAME,TYPE | awk '$2 == "disk" { print $1 }') + [ "${#disks[@]}" -gt 0 ] || { die 'No whole disks were detected.'; return 1; } + printf '\nDetected disks:\n\n' + printf '%-4s %-18s %-10s %-24s %-16s %s\n' '#' DEVICE SIZE MODEL SERIAL TRAN + disk_info | nl -w2 -s' ' + printf '\nEnter the number shown above, or q to quit: ' + read -r choice + [ "$choice" = q ] && exit 0 + [[ "$choice" =~ ^[0-9]+$ ]] || { die 'Invalid selection.'; return 1; } + target=${disks[$((choice - 1))]:-} + [ -n "$target" ] || { die 'Invalid selection.'; return 1; } +} + +validate_target() { + [ -b "$target" ] || { die 'Selected path is not a block device.'; return 1; } + [ "$(lsblk -dnro TYPE "$target")" = disk ] || { die 'Only whole disks may be erased.'; return 1; } + [ "$(lsblk -dnro RO "$target")" = 0 ] || { die 'The disk is read-only.'; return 1; } + mounted "$target" && { die 'The disk or one of its partitions is mounted.'; return 1; } + is_live_device "$target" && { die 'The live boot device cannot be erased.'; return 1; } +} + +confirm() { + local info + info=$(lsblk -dnpo NAME,SIZE,MODEL,SERIAL "$target") + printf '\nTARGET: %s\n%s\n\nThis permanently destroys all data. Type ERASE to continue: ' "$target" "$info" + read -r answer + [ "$answer" = ERASE ] || { printf 'Cancelled.\n'; return 1; } +} + +erase_ssd() { + printf '\nSSD/NVMe erase options:\n1) Secure discard (blkdiscard --secure)\n2) NVMe sanitize (NVMe only)\n3) Cancel\nChoose: ' + read -r method + case "$method" in + 1) + blkdiscard --secure --force "$target" || { die 'Secure discard failed; no data was overwritten.'; return 1; } + ;; + 2) + [[ "$target" == /dev/nvme*n* ]] || { die 'NVMe sanitize requires an NVMe namespace.'; return 1; } + nvme sanitize "$target" -a 2 || { die 'NVMe sanitize failed.'; return 1; } + nvme sanitize-log "$target" -H || true + ;; + *) printf 'Cancelled.\n'; return 1 ;; + esac +} + +erase_hdd() { + printf '\nHDD erase uses three random overwrite passes plus a zero pass. Continue? [yes/no]: ' + read -r answer + [ "$answer" = yes ] || { printf 'Cancelled.\n'; return 1; } + shred --verbose --iterations=3 --zero --force "$target" +} + +verify() { + printf '\nFinal device state:\n' + lsblk -dnpo NAME,TYPE,SIZE,MODEL,SERIAL "$target" + printf 'Erase command completed. Verification is limited to command/device status.\n' +} + +main() { + [ "$(id -u)" -eq 0 ] || { echo 'This tool must run as root.' >&2; exit 1; } + printf '\nEP ERASER - destructive disk sanitization\n==========================================\n' + while true; do + choose_disk || continue + validate_target || continue + rotational=$(lsblk -dnro ROTA "$target") + if [ "$rotational" = 1 ]; then + operation=HDD + else + operation=SSD/NVMe + fi + printf '\nDetected media type: %s\n' "$operation" + confirm || continue + if [ "$operation" = HDD ]; then erase_hdd; else erase_ssd; fi + verify + printf '\nPress Enter to return to the disk menu, or type q to quit: ' + read -r answer + [ "$answer" = q ] && exit 0 + done +} + +main "$@" \ No newline at end of file diff --git a/config/package-lists/ep-eraser.list.chroot b/config/package-lists/ep-eraser.list.chroot new file mode 100644 index 0000000..ce324f4 --- /dev/null +++ b/config/package-lists/ep-eraser.list.chroot @@ -0,0 +1,8 @@ +bash +coreutils +hdparm +nvme-cli +parted +smartmontools +util-linux +whiptail \ No newline at end of file