Compare commits

..

5 Commits

Author SHA1 Message Date
Cayo Puigdefabregas 629a2a172e change EreuseWorkbench for workbench-script 2024-10-21 11:30:05 +02:00
pedro 9aa156628b prepare a first integration of usody-sanitize
in session with cayo we see this just works for Monday
2024-10-18 18:32:07 +02:00
Cayo Puigdefabregas efe028aea1 deactivate confirm 2024-10-18 13:05:44 +02:00
Cayo Puigdefabregas 731567b8ea use of sanitize in workbench-script 2024-10-18 12:16:00 +02:00
pedro be28ec2a1f move alternate erase functions to docs 2024-10-18 10:54:45 +02:00
8 changed files with 209 additions and 297 deletions

View File

@ -44,14 +44,20 @@ boot_iso_uefi_secureboot:
-drive file=deploy/iso/workbench_debug.iso,cache=none,if=virtio,format=raw,index=0,media=disk \ -drive file=deploy/iso/workbench_debug.iso,cache=none,if=virtio,format=raw,index=0,media=disk \
-boot menu=on -boot menu=on
# when you change something, you need to refresh it this way test_usody_sanitize:
regenerate_pxe_install: # TODO adapt settings accordingly for this test
./deploy-workbench.sh # ERASE=y ./deploy-workbench.sh
pxe/install-pxe.sh # create 3 disks for testing
qemu-img create -f raw test_sanitize_disk1.img 1G
qemu-img create -f raw test_sanitize_disk2.img 1G
qemu-img create -f raw test_sanitize_disk3.img 1G
sudo qemu-system-x86_64 \
-enable-kvm -m 2G -vga qxl -netdev user,id=wan -device virtio-net,netdev=wan,id=nic1 \
-drive format=raw,file=iso/workbench_debug.iso,cache=none,if=virtio \
-drive format=raw,file=test_sanitize_disk1.img,cache=none,if=virtio \
-drive format=raw,file=test_sanitize_disk2.img,cache=none,if=virtio \
-drive format=raw,file=test_sanitize_disk3.img,cache=none,if=virtio
es_gen:
$(MAKE) es_gen_po
$(MAKE) es_gen_mo
es_gen_po: es_gen_po:
cp locale/es/LC_MESSAGES/messages.po locale/es/LC_MESSAGES/messages.pot.bak cp locale/es/LC_MESSAGES/messages.po locale/es/LC_MESSAGES/messages.pot.bak

View File

@ -199,12 +199,12 @@ create_persistence_partition() {
mkdir -p "${tmp_rw_mount}" mkdir -p "${tmp_rw_mount}"
${SUDO} mount "$(pwd)/${rw_img_path}" "${tmp_rw_mount}" ${SUDO} mount "$(pwd)/${rw_img_path}" "${tmp_rw_mount}"
${SUDO} mkdir -p "${tmp_rw_mount}" ${SUDO} mkdir -p "${tmp_rw_mount}"
if [ ! -f "settings.ini" ]; then if [ -f "settings.ini" ]; then
${SUDO} cp -v settings.ini.example settings.ini ${SUDO} cp -v settings.ini "${tmp_rw_mount}/settings.ini"
echo "WARNING: settings.ini was not there, settings.ini.example was copied, this only happens once" else
echo "ERROR: settings.ini does not exist yet, cannot read config from there. You can take inspiration with file settings.ini.example"
exit 1
fi fi
${SUDO} cp -v settings.ini "${tmp_rw_mount}/settings.ini"
${SUDO} umount "${tmp_rw_mount}" ${SUDO} umount "${tmp_rw_mount}"
uuid="$(blkid "${rw_img_path}" | awk '{ print $3; }')" uuid="$(blkid "${rw_img_path}" | awk '{ print $3; }')"
@ -253,27 +253,6 @@ END2
END END
)" )"
# thanks https://wiki.debian.org/Keyboard
chroot_kbd_conf_str="$(cat<<END
chroot_kbd_conf() {
###################
# configure keyboard
cat > /etc/default/keyboard <<END2
# KEYBOARD CONFIGURATION FILE
# generated by deploy-workbench.sh
# Consult the keyboard(5) manual page.
XKBMODEL="pc105"
XKBLAYOUT="\${CUSTOM_LANG}"
BACKSPACE="guess"
END2
}
END
)"
prepare_app() { prepare_app() {
# prepare app during prepare_chroot_env # prepare app during prepare_chroot_env
workbench_dir="${ISO_PATH}/chroot/opt/workbench" workbench_dir="${ISO_PATH}/chroot/opt/workbench"
@ -285,6 +264,8 @@ prepare_app() {
# startup script execution # startup script execution
cat > "${ISO_PATH}/chroot/root/.profile" <<END cat > "${ISO_PATH}/chroot/root/.profile" <<END
# pipx path for usody-sanitize
PATH="${PATH}:/root/.local/bin"
if [ -f /tmp/workbench_lock ]; then if [ -f /tmp/workbench_lock ]; then
return 0 return 0
else else
@ -301,15 +282,13 @@ if [ "\${nfs_host}" ]; then
mount --bind /run/live/medium /mnt mount --bind /run/live/medium /mnt
# debian live nfs path is readonly, do a trick # debian live nfs path is readonly, do a trick
# to make snapshots subdir readwrite # to make snapshots subdir readwrite
mount -v \${nfs_host}:/snapshots /run/live/medium/snapshots mount \${nfs_host}:/snapshots /run/live/medium/snapshots
# reload mounts on systemd # reload mounts on systemd
systemctl daemon-reload systemctl daemon-reload
fi fi
# clearly specify the right working directory, used in the python script as os.getcwd() # clearly specify the right working directory, used in the python script as os.getcwd()
cd /mnt cd /mnt
#pipenv run python /opt/workbench/workbench-script.py --config /mnt/settings.ini pipenv run python /opt/workbench/workbench-script.py --config /mnt/settings.ini
# works meanwhile this project is vanilla python
python /opt/workbench/workbench-script.py --config /mnt/settings.ini
stty echo stty echo
set +x set +x
@ -325,16 +304,18 @@ echo 'Install requirements'
# Install debian requirements # Install debian requirements
apt-get install -y --no-install-recommends \ apt-get install -y --no-install-recommends \
sudo locales keyboard-configuration console-setup qrencode \ sudo locales \
python-is-python3 python3 python3-dev python3-pip pipenv \ python-is-python3 python3 python3-dev python3-pip pipenv \
dmidecode smartmontools hwinfo pciutils lshw nfs-common < /dev/null dmidecode smartmontools hwinfo pciutils lshw nfs-common pipx < /dev/null
pipx install usody-sanitize
# Install lshw B02.19 utility using backports (DEPRECATED in Debian 12) # Install lshw B02.19 utility using backports (DEPRECATED in Debian 12)
#apt install -y -t ${VERSION_CODENAME}-backports lshw < /dev/null #apt install -y -t ${VERSION_CODENAME}-backports lshw < /dev/null
echo 'Install sanitize requirements' echo 'Install usody-sanitize requirements'
# Install sanitize debian requirements # Install usody-sanitize debian requirements
apt-get install -y --no-install-recommends \ apt-get install -y --no-install-recommends \
hdparm nvme-cli < /dev/null hdparm nvme-cli < /dev/null
@ -383,15 +364,8 @@ ${install_app_str}
# thanks src https://serverfault.com/questions/362903/how-do-you-set-a-locale-non-interactively-on-debian-ubuntu # thanks src https://serverfault.com/questions/362903/how-do-you-set-a-locale-non-interactively-on-debian-ubuntu
export LANG=${LANG} export LANG=${LANG}
export LC_ALL=${LANG} export LC_ALL=${LANG}
echo "${MYLOCALE}" > /etc/locale.gen
# Generate the locale
locale-gen
# feeds /etc/default/locale for the shell env var
update-locale LANG=${LANG} LC_ALL=${LANG}
# this is a high level command that does locale-gen and update-locale altogether # this is a high level command that does locale-gen and update-locale altogether
# but it is too interactive dpkg-reconfigure --frontend=noninteractive locales
#dpkg-reconfigure --frontend=noninteractive locales
# DEBUG
locale -a locale -a
# Autologin root user # Autologin root user
@ -416,9 +390,6 @@ apt-get install -y --no-install-recommends \
< /dev/null < /dev/null
${chroot_netdns_conf_str} ${chroot_netdns_conf_str}
CUSTOM_LANG=${CUSTOM_LANG}
${chroot_kbd_conf_str}
chroot_kbd_conf
# Set up root user # Set up root user
# this is the root password # this is the root password
@ -438,19 +409,7 @@ CHROOT
} }
prepare_chroot_env() { prepare_chroot_env() {
CUSTOM_LANG="${CUSTOM_LANG:-es}" LANG="${CUSTOM_LANG:-es_ES.UTF-8}"
case "${CUSTOM_LANG}" in
es)
export LANG="es_ES.UTF-8"
export MYLOCALE="${LANG} UTF-8"
;;
en)
export LANG="en_US.UTF-8"
;;
*)
echo "ERROR: CUSTOM_LANG not supported. Available: es"
exit 1
esac
# version of debian the bootstrap is going to build # version of debian the bootstrap is going to build
# if no VERSION_CODENAME is specified we assume that the bootstrap is going to # if no VERSION_CODENAME is specified we assume that the bootstrap is going to
# be build with the same version of debian being executed because some files # be build with the same version of debian being executed because some files
@ -474,7 +433,6 @@ prepare_chroot_env() {
prepare_app prepare_app
} }
# thanks https://willhaley.com/blog/custom-debian-live-environment/ # thanks https://willhaley.com/blog/custom-debian-live-environment/
install_requirements() { install_requirements() {
# Install requirements # Install requirements

112
docs/dev-es.md Normal file
View File

@ -0,0 +1,112 @@
## borrado minimalista
Un enfoque inicial que teníamos para el borrado de disco son las siguientes funciones, esto lo hemos descartado para usar una herramienta más avanzada en el borrado [usody-sanitize](https://github.com/usody/sanitize/)
```python
## Xavier Functions ##
def erase_basic(disk):
"""
Basic Erasure
https://tsapps.nist.gov/publication/get_pdf.cfm?pub_id=917935
Settings for basic data erasure using shred Linux command.
A software-based fast non-100%-secured way of erasing data storage.
Performs 1 pass overwriting one round using all zeros.
Compliant with NIST SP-800-8y8.
In settings appear:
WB_ERASE = EraseBasic
WB_ERASE_STEPS = 1
WB_ERASE_LEADING_ZEROS = False
"""
cmd = f'shred -vn 1 /dev/{disk}'
return [exec_cmd_erase(cmd)]
def erase_baseline(disk):
"""
Baseline Secure Erasure
Settings for advanced data erasure using badblocks Linux software.
A secured-way of erasing data storages, erase hidden areas,
checking the erase sector by sector.
Performs 1 pass overwriting each sector with zeros and a final verification.
Compliant with HMG Infosec Standard 5 Baseline.
In settings appear:
WB_ERASE = EraseSectors
WB_ERASE_STEPS = 1
WB_ERASE_LEADING_ZEROS = True
WB_ERASE_1_METHOD = EraseBasic
WB_ERASE_1_STEP_TYPE = 0
WB_ERASE_2_METHOD = EraseSectors
WB_ERASE_2_STEP_TYPE = 1
"""
result = []
cmd = f'shred -zvn 0 /dev/{disk}'
result.append(exec_cmd_erase(cmd))
cmd = f'badblocks -st random -w /dev/{disk}'
result.append(exec_cmd_erase(cmd))
return result
def erase_enhanced(disk):
"""
Enhanced Secure Erasure
Settings for advanced data erasure using badblocks Linux software.
A secured-way of erasing data storages, erase hidden areas,
checking the erase sector by sector.
Performs 3 passes overwriting every sector with zeros and ones,
and final verification. Compliant with HMG Infosec Standard 5 Enhanced.
In settings appear:
WB_ERASE = EraseSectors
WB_ERASE_LEADING_ZEROS = True
WB_ERASE_1_METHOD = EraseBasic
WB_ERASE_1_STEP_TYPE = 1
WB_ERASE_2_METHOD = EraseBasic
WB_ERASE_2_STEP_TYPE = 0
WB_ERASE_3_METHOD = EraseSectors
WB_ERASE_3_STEP_TYPE = 1
"""
result = []
cmd = f'shred -vn 1 /dev/{disk}'
result.append(exec_cmd_erase(cmd))
cmd = f'shred -zvn 0 /dev/{disk}'
result.append(exec_cmd_erase(cmd))
## creo que realmente seria asi (3 pases y una extra poniendo a ceros):
# shred -zvn 3 /def/{disk}
# tampoco estoy seguro que el badblocks haga un proceso de verificacion.
cmd = f'badblocks -st random -w /dev/{disk}'
result.append(exec_cmd_erase(cmd))
return result
## End Xavier Functions ##
## Erase Functions ##
def ata_secure_erase_null(disk):
cmd_baseline = f'hdparm --user-master u --security-erase NULL /dev/{disk}'
return [exec_cmd_erase(cmd_baseline)]
def ata_secure_erase_enhanced(disk):
cmd_enhanced = f'hdparm --user-master u --security-erase-enhanced /dev/{disk}'
return [exec_cmd_erase(cmd_enhanced)]
def nvme_secure_erase(disk):
cmd_encrypted = f'nvme format /dev/{disk} --ses=1'
return [exec_cmd_erase(cmd_encrypted)]
## End Erase Functions ##
```

Binary file not shown.

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-08 18:25+0100\n" "POT-Creation-Date: 2024-10-15 21:15+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -17,19 +17,19 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
#: workbench-script.py:49 workbench-script.py:54 #: workbench-script.py:48 workbench-script.py:53
msgid "Running command `%s`" msgid "Running command `%s`"
msgstr "Ejecutando comando `%s`" msgstr "Ejecutando comando `%s`"
#: workbench-script.py:279 #: workbench-script.py:284
msgid "Created snapshots directory at '%s'" msgid "Created snapshots directory at '%s'"
msgstr "Creado directorio de snapshots en '%s'" msgstr "Creado directorio de snapshots en '%s'"
#: workbench-script.py:282 #: workbench-script.py:287
msgid "Snapshot written in path '%s'" msgid "Snapshot written in path '%s'"
msgstr "Snapshot escrito en ruta '%s'" msgstr "Snapshot escrito en ruta '%s'"
#: workbench-script.py:285 #: workbench-script.py:290
msgid "" msgid ""
"Attempting to save file in actual path. Reason: Failed to write in snapshots " "Attempting to save file in actual path. Reason: Failed to write in snapshots "
"directory:\n" "directory:\n"
@ -39,11 +39,11 @@ msgstr ""
"escribir en el directorio de snapshots:\n" "escribir en el directorio de snapshots:\n"
" %s." " %s."
#: workbench-script.py:292 #: workbench-script.py:297
msgid "Snapshot written in fallback path '%s'" msgid "Snapshot written in fallback path '%s'"
msgstr "Snapshot escrito en ruta alternativa '%s'" msgstr "Snapshot escrito en ruta alternativa '%s'"
#: workbench-script.py:294 #: workbench-script.py:299
msgid "" msgid ""
"Could not save snapshot locally. Reason: Failed to write in fallback path:\n" "Could not save snapshot locally. Reason: Failed to write in fallback path:\n"
" %s" " %s"
@ -52,53 +52,49 @@ msgstr ""
"alternativa:\n" "alternativa:\n"
" %s" " %s"
#: workbench-script.py:317 #: workbench-script.py:316
msgid "Snapshot successfully sent to '%s'" msgid "Snapshot successfully sent to '%s'"
msgstr "Snapshot enviado con éxito a '%s'" msgstr "Snapshot enviado con éxito a '%s'"
#: workbench-script.py:335 #: workbench-script.py:331
msgid "Snapshot %s could not be sent to URL '%s'"
msgstr "Snapshot %s no se pudo enviar a la URL '%s'"
#: workbench-script.py:338
msgid "" msgid ""
"Snapshot %s not remotely sent to URL '%s'. Do you have internet? Is your " "Snapshot not remotely sent to URL '%s'. Do you have internet? Is your server "
"server up & running? Is the url token authorized?\n" "up & running? Is the url token authorized?\n"
" %s" " %s"
msgstr "" msgstr ""
"Snapshot %s no enviado remotamente a la URL '%s'. Tienes internet? Está el " "Snapshot no enviado remotamente a la URL '%s'. Tienes internet? Está el "
"servidor en marcha? Está autorizado el url token?\n" "servidor en marcha? Está autorizado el url token?\n"
" %s" " %s"
#: workbench-script.py:350 #: workbench-script.py:342
msgid "Found config file in path: %s." msgid "Found config file in path: %s."
msgstr "Encontrado fichero de configuración en ruta: %s." msgstr "Encontrado fichero de configuración en ruta: %s."
#: workbench-script.py:361 #: workbench-script.py:353
msgid "Config file '%s' not found. Using default values." msgid "Config file '%s' not found. Using default values."
msgstr "" msgstr ""
"Fichero de configuración '%s' no encontrado. Utilizando valores por defecto." "Fichero de configuración '%s' no encontrado. Utilizando valores por defecto."
#: workbench-script.py:379 #: workbench-script.py:373
msgid "workbench-script.py [-h] [--config CONFIG]" msgid "workbench-script.py [-h] [--config CONFIG]"
msgstr "" msgstr ""
#: workbench-script.py:380 #: workbench-script.py:374
msgid "Optional config loader for workbench." msgid "Optional config loader for workbench."
msgstr "Cargador opcional de configuración para workbench" msgstr "Cargador opcional de configuración para workbench"
#: workbench-script.py:383 #: workbench-script.py:377
msgid "" msgid ""
"path to the config file. Defaults to 'settings.ini' in the current directory." "path to the config file. Defaults to 'settings.ini' in the current directory."
msgstr "" msgstr ""
"ruta al fichero de configuración. Por defecto es 'settings.ini' en el " "ruta al fichero de configuración. Por defecto es 'settings.ini' en el "
"directorio actual" "directorio actual"
#: workbench-script.py:416 #: workbench-script.py:410
msgid "START" msgid "START"
msgstr "INICIO" msgstr "INICIO"
#: workbench-script.py:430 #: workbench-script.py:423
msgid "" msgid ""
"This script must be run as root. Collected data will be incomplete or " "This script must be run as root. Collected data will be incomplete or "
"unusable" "unusable"
@ -106,6 +102,6 @@ msgstr ""
"Es conveniente que este script sea ejecutado como administrador (root). Los " "Es conveniente que este script sea ejecutado como administrador (root). Los "
"datos recopilados serán incompletos o no usables." "datos recopilados serán incompletos o no usables."
#: workbench-script.py:448 #: workbench-script.py:441
msgid "END" msgid "END"
msgstr "FIN" msgstr "FIN"

View File

@ -37,7 +37,7 @@ backup_file() {
if [ -f "${target}" ]; then if [ -f "${target}" ]; then
if ! grep -q 'we should do a backup' "${target}"; then if ! grep -q 'we should do a backup' "${target}"; then
${SUDO} cp -v -a "${target}" "${target}-bak_${ts}" ${SUDO} cp -a "${target}" "${target}-bak_${ts}"
fi fi
fi fi
} }
@ -69,14 +69,14 @@ END
# reload nfs exports # reload nfs exports
${SUDO} exportfs -vra ${SUDO} exportfs -vra
if [ ! -f ./settings.ini ]; then
cp -v ./settings.ini.example ./settings.ini
echo "WARNING: settings.ini was not there, settings.ini.example was copied, this only happens once"
fi
if [ ! -f "${nfs_path}/settings.ini" ]; then if [ ! -f "${nfs_path}/settings.ini" ]; then
${SUDO} cp -v settings.ini "${nfs_path}/settings.ini" if [ -f "settings.ini" ]; then
echo "WARNING: ${nfs_path}/settings.ini was not there, ./settings.ini was copied, this only happens once" ${SUDO} cp settings.ini "${nfs_path}/settings.ini"
else
echo "ERROR: $(pwd)/settings.ini does not exist yet, cannot read config from there. You can take inspiration with file $(pwd)/settings.ini.example"
exit 1
fi
fi fi
} }
@ -93,7 +93,6 @@ pxe-service=x86PC,"Network Boot",pxelinux
enable-tftp enable-tftp
tftp-root=${tftp_path} tftp-root=${tftp_path}
END END
sudo systemctl restart dnsmasq || true
} }
install_netboot() { install_netboot() {
@ -111,12 +110,8 @@ install_netboot() {
${SUDO} cp -fv "${PXE_DIR}/../iso/staging/live/vmlinuz" "${tftp_path}/" ${SUDO} cp -fv "${PXE_DIR}/../iso/staging/live/vmlinuz" "${tftp_path}/"
${SUDO} cp -fv "${PXE_DIR}/../iso/staging/live/initrd" "${tftp_path}/" ${SUDO} cp -fv "${PXE_DIR}/../iso/staging/live/initrd" "${tftp_path}/"
${SUDO} cp -v /usr/lib/syslinux/memdisk "${tftp_path}/" ${SUDO} cp /usr/lib/syslinux/memdisk "${tftp_path}/"
${SUDO} cp -v /usr/lib/syslinux/modules/bios/* "${tftp_path}/" ${SUDO} cp /usr/lib/syslinux/modules/bios/* "${tftp_path}/"
if [ ! -f ./pxe-menu.cfg ]; then
${SUDO} cp -v ./pxe-menu.cfg.example pxe-menu.cfg
echo "WARNING: pxe-menu.cfg was not there, pxe-menu.cfg.example was copied, this only happens once"
fi
envsubst < ./pxe-menu.cfg | ${SUDO} tee "${tftp_path}/pxelinux.cfg/default" envsubst < ./pxe-menu.cfg | ${SUDO} tee "${tftp_path}/pxelinux.cfg/default"
fi fi
@ -133,11 +128,11 @@ init_config() {
PXE_DIR="$(pwd)" PXE_DIR="$(pwd)"
if [ ! -f ./.env ]; then if [ -f ./.env ]; then
cp -v ./.env.example ./.env . ./.env
echo "WARNING: .env was not there, .env.example was copied, this only happens once" else
echo "PXE: WARNING: $(pwd)/.env does not exist yet, cannot read config from there. You can take inspiration with file $(pwd)/.env.example"
fi fi
. ./.env
VERSION_CODENAME="${VERSION_CODENAME:-bookworm}" VERSION_CODENAME="${VERSION_CODENAME:-bookworm}"
tftp_path="${tftp_path:-/srv/pxe-tftp}" tftp_path="${tftp_path:-/srv/pxe-tftp}"
# vars used in envsubst require to be exported: # vars used in envsubst require to be exported:

View File

@ -1,10 +1,7 @@
[settings] [settings]
url = http://localhost:8000/api/v1/snapshot/ url = http://localhost:8000/api/snapshot/
#url = https://demo.ereuse.org/api/v1/snapshot/ token = '1234'
# sample token that works with default deployment such as the previous two urls
token = 5018dd65-9abd-4a62-8896-80f34ac66150
# path = /path/to/save # path = /path/to/save
# device = your_device_name # device = your_device_name
# # erase = basic # # erase = basic
# legacy = True # legacy = true

View File

@ -6,7 +6,6 @@ import uuid
import hashlib import hashlib
import argparse import argparse
import configparser import configparser
import urllib.parse
import urllib.request import urllib.request
import gettext import gettext
@ -17,7 +16,6 @@ from datetime import datetime
## Legacy Functions ## ## Legacy Functions ##
def convert_to_legacy_snapshot(snapshot): def convert_to_legacy_snapshot(snapshot):
snapshot["sid"] = str(uuid.uuid4()).split("-")[0] snapshot["sid"] = str(uuid.uuid4()).split("-")[0]
snapshot["software"] = "workbench-script" snapshot["software"] = "workbench-script"
@ -26,8 +24,8 @@ def convert_to_legacy_snapshot(snapshot):
snapshot["settings_version"] = "No Settings Version (NaN)" snapshot["settings_version"] = "No Settings Version (NaN)"
snapshot["timestamp"] = snapshot["timestamp"].replace(" ", "T") snapshot["timestamp"] = snapshot["timestamp"].replace(" ", "T")
snapshot["data"]["smart"] = snapshot["data"]["disks"] snapshot["data"]["smart"] = snapshot["data"]["disks"]
snapshot["data"]["lshw"] = json.loads(snapshot["data"]["lshw"])
snapshot["data"].pop("disks") snapshot["data"].pop("disks")
snapshot.pop("code")
snapshot.pop("erase") snapshot.pop("erase")
## End Legacy Functions ## ## End Legacy Functions ##
@ -56,6 +54,11 @@ def exec_cmd_erase(cmd):
return '' return ''
# return os.popen(cmd).read() # return os.popen(cmd).read()
def gen_code():
uid = str(uuid.uuid4()).encode('utf-8')
return hashlib.shake_256(uid).hexdigest(3)
## End Utility functions ## ## End Utility functions ##
@ -63,119 +66,14 @@ SNAPSHOT_BASE = {
'timestamp': str(datetime.now()), 'timestamp': str(datetime.now()),
'type': 'Snapshot', 'type': 'Snapshot',
'uuid': str(uuid.uuid4()), 'uuid': str(uuid.uuid4()),
'code': gen_code(),
'software': "workbench-script", 'software': "workbench-script",
'version': "0.0.1", 'version': "0.0.1",
'data': {}, 'data': {},
'erase': [] 'erase': []
} }
## Command Functions ## ## Command Functions ##
## Erase Functions ##
## Xavier Functions ##
def erase_basic(disk):
"""
Basic Erasure
https://tsapps.nist.gov/publication/get_pdf.cfm?pub_id=917935
Settings for basic data erasure using shred Linux command.
A software-based fast non-100%-secured way of erasing data storage.
Performs 1 pass overwriting one round using all zeros.
Compliant with NIST SP-800-8y8.
In settings appear:
WB_ERASE = EraseBasic
WB_ERASE_STEPS = 1
WB_ERASE_LEADING_ZEROS = False
"""
cmd = f'shred -vn 1 /dev/{disk}'
return [exec_cmd_erase(cmd)]
def erase_baseline(disk):
"""
Baseline Secure Erasure
Settings for advanced data erasure using badblocks Linux software.
A secured-way of erasing data storages, erase hidden areas,
checking the erase sector by sector.
Performs 1 pass overwriting each sector with zeros and a final verification.
Compliant with HMG Infosec Standard 5 Baseline.
In settings appear:
WB_ERASE = EraseSectors
WB_ERASE_STEPS = 1
WB_ERASE_LEADING_ZEROS = True
WB_ERASE_1_METHOD = EraseBasic
WB_ERASE_1_STEP_TYPE = 0
WB_ERASE_2_METHOD = EraseSectors
WB_ERASE_2_STEP_TYPE = 1
"""
result = []
cmd = f'shred -zvn 0 /dev/{disk}'
result.append(exec_cmd_erase(cmd))
cmd = f'badblocks -st random -w /dev/{disk}'
result.append(exec_cmd_erase(cmd))
return result
def erase_enhanced(disk):
"""
Enhanced Secure Erasure
Settings for advanced data erasure using badblocks Linux software.
A secured-way of erasing data storages, erase hidden areas,
checking the erase sector by sector.
Performs 3 passes overwriting every sector with zeros and ones,
and final verification. Compliant with HMG Infosec Standard 5 Enhanced.
In settings appear:
WB_ERASE = EraseSectors
WB_ERASE_LEADING_ZEROS = True
WB_ERASE_1_METHOD = EraseBasic
WB_ERASE_1_STEP_TYPE = 1
WB_ERASE_2_METHOD = EraseBasic
WB_ERASE_2_STEP_TYPE = 0
WB_ERASE_3_METHOD = EraseSectors
WB_ERASE_3_STEP_TYPE = 1
"""
result = []
cmd = f'shred -vn 1 /dev/{disk}'
result.append(exec_cmd_erase(cmd))
cmd = f'shred -zvn 0 /dev/{disk}'
result.append(exec_cmd_erase(cmd))
## creo que realmente seria asi (3 pases y una extra poniendo a ceros):
# shred -zvn 3 /def/{disk}
# tampoco estoy seguro que el badblocks haga un proceso de verificacion.
cmd = f'badblocks -st random -w /dev/{disk}'
result.append(exec_cmd_erase(cmd))
return result
## End Xavier Functions ##
def ata_secure_erase_null(disk):
cmd_baseline = f'hdparm --user-master u --security-erase NULL /dev/{disk}'
return [exec_cmd_erase(cmd_baseline)]
def ata_secure_erase_enhanced(disk):
cmd_enhanced = f'hdparm --user-master u --security-erase-enhanced /dev/{disk}'
return [exec_cmd_erase(cmd_enhanced)]
def nvme_secure_erase(disk):
cmd_encrypted = f'nvme format /dev/{disk} --ses=1'
return [exec_cmd_erase(cmd_encrypted)]
## End Erase Functions ##
@logs @logs
def get_disks(): def get_disks():
@ -184,39 +82,13 @@ def get_disks():
) )
return disks.get('blockdevices', []) return disks.get('blockdevices', [])
@logs @logs
def gen_erase(all_disks, type_erase, user_disk=None): def gen_erase(type_erase, user_disk=None):
erase = [] if user_disk:
for disk in all_disks: return exec_cmd(f"sanitize -d {user_disk} -m {type_erase}")
if user_disk and disk['name'] not in user_disk: return exec_cmd(f"sanitize -a -m {type_erase}")
continue # return exec_cmd(f"sanitize -a -m {type_erase} --confirm")
if disk['type'] != 'disk':
continue
if 'boot' in disk['mountpoints']:
continue
if not disk['rota']:
# if soport nvme erase
erase.append(nvme_secure_erase(disk['name']))
elif disk['tran'] in ['ata', 'sata']:
# if soport ata erase
if type_erase == 'basic':
erase.append(ata_secure_erase_null(disk['name']))
elif type_erase == 'baseline':
erase.append(ata_secure_erase_null(disk['name']))
elif type_erase == 'enhanced':
erase.append(ata_secure_erase_enhanced(disk['name']))
else:
# For old disks
if type_erase == 'basic':
erase.append(erase_basic(disk['name']))
elif type_erase == 'baseline':
erase.append(erase_baseline(disk['name']))
elif type_erase == 'enhanced':
erase.append(erase_enhanced(disk['name']))
return erase
@logs @logs
@ -296,13 +168,7 @@ def save_snapshot_in_disk(snapshot, path):
# TODO sanitize url, if url is like this, it fails # TODO sanitize url, if url is like this, it fails
# url = 'http://127.0.0.1:8000/api/snapshot/' # url = 'http://127.0.0.1:8000/api/snapshot/'
def send_snapshot_to_devicehub(snapshot, token, url, legacy): def send_snapshot_to_devicehub(snapshot, token, url):
url_components = urllib.parse.urlparse(url)
ev_path = "evidence/{}".format(snapshot["uuid"])
components = (url_components.scheme, url_components.netloc, ev_path, '', '', '')
ev_url = urllib.parse.urlunparse(components)
# apt install qrencode
headers = { headers = {
"Authorization": f"Bearer {token}", "Authorization": f"Bearer {token}",
"Content-Type": "application/json" "Content-Type": "application/json"
@ -316,35 +182,21 @@ def send_snapshot_to_devicehub(snapshot, token, url, legacy):
if 200 <= status_code < 300: if 200 <= status_code < 300:
logger.info(_("Snapshot successfully sent to '%s'"), url) logger.info(_("Snapshot successfully sent to '%s'"), url)
if legacy:
try: try:
response = json.loads(response_text) response = json.loads(response_text)
public_url = response.get('public_url') if response.get('url'):
dhid = response.get('dhid') # apt install qrencode
if public_url: qr = "echo {} | qrencode -t ANSI".format(response['url'])
# apt install qrencode
qr = "echo {} | qrencode -t ANSI".format(public_url)
print(exec_cmd(qr))
print("url: {}".format(public_url))
if dhid:
print("dhid: {}".format(dhid))
except Exception:
logger.error(response_text)
else:
qr = "echo {} | qrencode -t ANSI".format(ev_url)
print(exec_cmd(qr)) print(exec_cmd(qr))
print(f"url: {ev_url}") print("url: {}".format(response['url']))
else: if response.get("dhid"):
logger.error(_("Snapshot %s could not be sent to URL '%s'"), snapshot["uuid"], url) print("dhid: {}".format(response['dhid']))
# TODO review all the try-except thing here; maybe the try inside legacy does not make sense anymore except Exception:
except urllib.error.HTTPError as e: logger.error(response_text)
error_details = e.read().decode('utf-8') # Get the error response body
logger.error(_("Snapshot %s not remotely sent to URL '%s'. Server responded with error:\n %s"),
snapshot["uuid"], url, error_details)
except Exception as e: except Exception as e:
logger.error(_("Snapshot %s not remotely sent to URL '%s'. Do you have internet? Is your server up & running? Is the url token authorized?\n %s"), snapshot["uuid"], url, e) logger.error(_("Snapshot not remotely sent to URL '%s'. Do you have internet? Is your server up & running? Is the url token authorized?\n %s"), url, e)
def load_config(config_file="settings.ini"): def load_config(config_file="settings.ini"):
""" """
@ -430,7 +282,6 @@ def main():
config_file = args.config config_file = args.config
config = load_config(config_file) config = load_config(config_file)
legacy = config.get("legacy")
# TODO show warning if non root, means data is not complete # TODO show warning if non root, means data is not complete
# if annotate as potentially invalid snapshot (pending the new API to be done) # if annotate as potentially invalid snapshot (pending the new API to be done)
@ -440,18 +291,15 @@ def main():
all_disks = get_disks() all_disks = get_disks()
snapshot = gen_snapshot(all_disks) snapshot = gen_snapshot(all_disks)
if config['erase'] and config['device'] and not config.get("legacy"): if config.get("legacy"):
snapshot['erase'] = gen_erase(all_disks, config['erase'], user_disk=config['device'])
elif config['erase'] and not config.get("legacy"):
snapshot['erase'] = gen_erase(all_disks, config['erase'])
if legacy:
convert_to_legacy_snapshot(snapshot) convert_to_legacy_snapshot(snapshot)
else:
snapshot['erase'] = gen_erase(config['erase'], user_disk=config['device'])
save_snapshot_in_disk(snapshot, config['path']) save_snapshot_in_disk(snapshot, config['path'])
if config['url']: if config['url']:
send_snapshot_to_devicehub(snapshot, config['token'], config['url'], legacy) send_snapshot_to_devicehub(snapshot, config['token'], config['url'])
logger.info(_("END")) logger.info(_("END"))