From 3b49a6c49e8d09f0719bc778df3b7bbdcb09e114 Mon Sep 17 00:00:00 2001 From: DarkFeather Date: Sat, 15 Jul 2023 22:34:00 -0500 Subject: [PATCH] Updating to include systemd timers Better Makefile layout Improving pytest --- .gitignore | 1 + Makefile | 77 ++++++++------------------------------------- PKGBUILD | 2 +- README.md | 15 ++++++--- aether-gen.bash | 24 +++++++------- aether-gen.service | 11 +++++++ aether-gen.timer | 11 +++++++ aether.service | 12 +++++++ aether.timer | 11 +++++++ installscript | 9 ++++++ remote-backup | 33 +++++++++++++++++-- tests/test_units.py | 4 +-- 12 files changed, 123 insertions(+), 87 deletions(-) create mode 100644 aether-gen.service create mode 100644 aether-gen.timer create mode 100644 aether.service create mode 100644 aether.timer create mode 100644 installscript diff --git a/.gitignore b/.gitignore index 9be8205..3539ebc 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ nodeslist *.tar.xz pkg/ src/ +**/__pycache__ diff --git a/Makefile b/Makefile index 8376eae..566655f 100644 --- a/Makefile +++ b/Makefile @@ -1,76 +1,25 @@ SHELL := /bin/bash BACKUPDIR := /usr/local/backup -client: user aether.bash - cp aether.bash /home/aether/aether.bash - /home/aether/aether.bash - sudo -u aether crontab -l > /tmp/cronie - grep aether.bash /tmp/cronie || echo '0 1 * * * /home/aether/aether.bash &>> /var/log/aether.log' >> /tmp/cronie - sudo -u aether crontab /tmp/cronie - rm /tmp/cronie - touch /var/log/aether.log - chown aether:aether /var/log/aether.log - make checkperm - compile: @echo Nothing to do install: compile - @echo You must specify client or server in a call to make. - -server: keys scripts user ./aether-gen.bash ./aether.pub ./server-backup - bash ./aether-gen.bash - cp ./aether.pub /home/aether/.ssh/authorized_keys - cp ./aether-gen.bash /root/bin/aether-gen.bash - mkdir -p /usr/local/etc/Aether/backup-entries - touch /usr/local/etc/Aether/nodeslist - mkdir -p ${BACKUPDIR}; - make checkperm - @echo You have the files. Add aether-gen.bash and server-backup to root\'s crontab. - @echo Mark services to be backed up by adding a file from examples/ to /usr/local/etc/Aether/backup-entries - @echo Track client nodes in /usr/local/etc/Aether/nodeslist - -user: aether make-user.bash - /bin/bash ./make-user.bash - -node-command: - @echo USE THE FOLLOWING COMMANDS TO SET UP A NODE. - @echo -ne 'git clone https://aninix.net/foundation/Aether;' - @echo - @echo -ne 'cd Aether; cat > aether # Paste the private key' - @echo - @echo -ne 'make client' - @echo - @echo MAKE SURE TO ADD THE NODE TO /usr/local/etc/Aether/nodes.list - -keys: - if [ ! -f ./aether ]; then ssh-keygen -t rsa -P "" -f aether; fi - -scripts: ./server-backup ./remote-backup - cp ./server-backup /root/bin - cp ./remote-backup /root/bin - make checkperm - -reverse: - if [ -f /root/bin/server-backup ]; then cp /root/bin/server-backup .; fi - if [ -f /root/bin/remote-backup ]; then cp /root/bin/remote-backup .; fi - if [ -f /root/bin/aether-gen.bash ]; then cp /root/bin/aether-gen.bash .; fi - if [ -f /home/aether/aether.bash ]; then cp /home/aether/aether.bash .; fi - -diff: - if [ -f /root/bin/server-backup ]; then diff ./server-backup /root/bin/server-backup; fi - if [ -f /root/bin/remote-backup ]; then diff ./remote-backup /root/bin/remote-backup; fi - if [ -f /root/bin/aether-gen.bash ]; then diff ./aether-gen.bash /root/bin/aether-gen.bash; fi - if [ -f /home/aether/aether.bash ]; then diff ./aether.bash /home/aether/aether.bash; fi + mkdir -p ${pkgdir}/usr/local/sbin + mkdir -p ${pkgdir}/usr/local/etc + install -m 0750 -o aether aether.bash ${pkgdir}/usr/local/sbin + install -m 0750 -o root -g root aether-gen.bash ${pkgdir}/usr/local/sbin + install -m 0750 -o root -g root remote-backup ${pkgdir}/usr/local/sbin + install -m 0750 -o aether -d ${pkgdir}/usr/local/etc/Aether + install -m 0750 -o aether -d ${pkgdir}/usr/local/etc/Aether/backup-entries + mkdir -p ${pkgdir}/usr/lib/systemd/system + for i in *.service *.timer; do install -m 0640 -o root -g root "$$i" ${pkgdir}/usr/lib/systemd/system; done checkperm: - id aether; - if [ -d /home/aether ]; then chmod 0600 /home/aether/.ssh/authorized_keys; chmod 0700 /home/aether/.ssh /home/aether; chown -R aether:aether /home/aether; fi - if [ -f /root/bin/remote-backup ]; then chmod 0700 /root/bin/remote-backup; chown root:root /root/bin/remote-backup; fi - if [ -f /root/bin/server-backup ]; then chmod 0700 /root/bin/server-backup; chown root:root /root/bin/server-backup; fi - if [ -d /usr/local/etc/Aether ]; then chmod -R 0700 /usr/local/etc/Aether; chown -R aether:aether /usr/local/etc/Aether; fi - if [ -d ${BACKUPDIR} ]; then chmod 0770 ${BACKUPDIR}; chown postgres:root ${BACKUPDIR}; fi - if [ -f /var/log/aether.log ]; then chown aether:aether /var/log/aether.log; chmod 0750 /var/log/aether.log; fi + for i in ${pkgdir}/usr/local/sbin/aether.bash ${pkgdir}/usr/local/sbin/aether-gen.bash ${pkgdir}/usr/local/sbin/remote-backup ${pkgdir}/usr/local/etc/Aether; do chmod 0750 "$$i"; done + for i in ${pkgdir}/usr/local/sbin/aether.bash ${pkgdir}/usr/local/sbin/aether-gen.bash ${pkgdir}/usr/local/sbin/remote-backup ${pkgdir}/usr/local/etc/Aether; do chown root: "$$i"; done + chown aether: ${pkgdir}/usr/local/sbin/aether.bash + for i in *.service *.timer; do chown root: ${pkgdir}/usr/lib/systemd/system; chmod 0640 ${pkgdir}/usr/lib/systemd/system; done clean: @bash -c 'printf "This will irreversibly destroy all backups. Confirm? [YES/no] " ; read answer; [ "$$answer" == "YES" ] && exit 0; exit 1' diff --git a/PKGBUILD b/PKGBUILD index 902729d..5e60ace 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -19,7 +19,7 @@ conflicts=() replaces=("${pkgname,,}", "aninix-${pkgname,,}") backup=() options=() -install= +install=installscript changelog= source=() noextract=() diff --git a/README.md b/README.md index 4c0f1dd..60769a6 100644 --- a/README.md +++ b/README.md @@ -10,20 +10,27 @@ You have two options to install this project: * Arch Linux and related distros: Run `makepkg -sri` * Other operating systems: Run `make install` -# Initial setup +## Initial setup To create the aether and aether.pub files, run "make keys". This should not be repeated. -# Adding backup configurations +## Adding backup configurations Individual projects wanting to be backed up by the Aether system should add a file to their package into `/usr/local/etc/Aether/backups/`. +## Tracking Nodes +A SIEM filter should be set up to search for successful logins of the `aether` user. + # Relevant Files and Software -Aether installs a script for rsync-based remote backups. We implement this policy through the two 4TB hard-drives, at least one of which is always off-site, that can be plugged into the [Maat](/Wiki/Hosts/Maat.md) hotswap bay along with a virtual machine that mounts the ArchLinux iso and the drive in the bay. +Aether installs a script for rsync-based remote backups. We implement this policy through the two 8TB hard-drives, at least one of which is always off-site, that can be plugged into a hotswap bay of a hypervisor along with a virtual machine that mounts the ArchLinux iso and the drive. Admins use the included ssh daemon in the iso to present the drive as a backup target, + +An additional backup is the generated `/home/aether/aether.enc` file. This is a more targeted backup of databases and file indexes. + +Keep in mind that all of [AniNIX/Foundation](https://foundation.aninix.net) is naturally a backup solution -- so long as anyone has a clone of the repo, the data survives. # Available Clients The only client is direct server access on one of the client nodes. # Equivalents or Competition -Equivalent services are DropBox, Google Drive, or Barracuda. +Equivalent services are DropBox, Google Drive, iCloud, or OneDrive. # Notes Those deploying Aether should maintain a nodeslist file that only root can read. diff --git a/aether-gen.bash b/aether-gen.bash index 129e613..3d14f97 100755 --- a/aether-gen.bash +++ b/aether-gen.bash @@ -1,31 +1,29 @@ #!/bin/bash -export LOGFILE="/var/log/aether-gen.log" +# File: aether-gen.bash +# +# Description: This file generates the backup in an encrypted format. +# +# Package: AniNIX/HelloWorld +# Copyright: WTFPL +# +# Author: DarkFeather export BACKUPDIR="/usr/local/backup" export BACKUPCMD="rsync -avzl --delete-after"; -date >> "$LOGFILE" chown root:root "$BACKUPDIR" chmod 0770 "$BACKUPDIR" for i in `find /usr/local/etc/Aether/backup-entries/ -type f`; do - bash "${i}" &>> "$LOGFILE" + bash "${i}" done date > "$BACKUPDIR"/lastbackup.date cd /home/aether -echo Creating and compressing archive... -tar cvf aether.tar /usr/local/backup -gzip -f aether.tar - -echo Encrypting archive -openssl enc -aes256 -pass file:/usr/local/etc/Aether/pass.txt -in aether.tar.gz -out aether.enc -rm aether.tar.gz +echo Creating and encrypting archive... +tar cvzf - /usr/local/backup | openssl enc -aes256 -pass file:/usr/local/etc/Aether/pass.txt -in aether.tar.gz -out aether.enc echo Created aether archive. - -date >> "$LOGFILE" -echo >> "$LOGFILE" diff --git a/aether-gen.service b/aether-gen.service new file mode 100644 index 0000000..15a36dd --- /dev/null +++ b/aether-gen.service @@ -0,0 +1,11 @@ +[Unit] +Description=AniNIX/Aether | Generation + +[Service] +Nice=19 +IOSchedulingClass=best-effort +IOSchedulingPriority=7 +Type=simple +ExecStart=/usr/local/sbin/aether-gen.bash + +#EOF diff --git a/aether-gen.timer b/aether-gen.timer new file mode 100644 index 0000000..f617995 --- /dev/null +++ b/aether-gen.timer @@ -0,0 +1,11 @@ +[Unit] +Description=AniNIX/Aether | Generation + +[Timer] +OnCalendar=01:00 +Persistent=false + +[Install] +WantedBy=timers.target + +#EOF diff --git a/aether.service b/aether.service new file mode 100644 index 0000000..bd18b78 --- /dev/null +++ b/aether.service @@ -0,0 +1,12 @@ +[Unit] +Description=AniNIX/Aether | Pull-back + +[Service] +Nice=19 +IOSchedulingClass=best-effort +IOSchedulingPriority=7 +Type=simple +ExecStart=/usr/local/sbin/aether +User=aether + +#EOF diff --git a/aether.timer b/aether.timer new file mode 100644 index 0000000..1864c5d --- /dev/null +++ b/aether.timer @@ -0,0 +1,11 @@ +[Unit] +Description=AniNIX/Aether | Pull-back + +[Timer] +OnCalendar=05:00 +Persistent=false + +[Install] +WantedBy=timers.target + +#EOF diff --git a/installscript b/installscript new file mode 100644 index 0000000..b6b8311 --- /dev/null +++ b/installscript @@ -0,0 +1,9 @@ +pre_install() { + groupadd aether &>/dev/null + useradd -g aether -d /srv/aether -s /bin/false aether &> /dev/null + chown -R aether:aether /srv/aether &> /dev/null +} + +pre_remove() { + getent passwd aether &>/dev/null && userdel aether &> /dev/null +} diff --git a/remote-backup b/remote-backup index bb38cff..1603c77 100644 --- a/remote-backup +++ b/remote-backup @@ -1,17 +1,44 @@ #!/bin/bash + +# File: HelloWorld.bash +# +# Description: This file exemplifies printing 'Hello world!' in bash. +# +# Package: AniNIX/HelloWorld +# Copyright: WTFPL +# +# Author: DarkFeather + +# Should only be done as root. if [ "$(whoami)" != "root" ]; then echo Needs to be run as root. exit 1; fi -if (ping -c 4 "$1" | grep -c ' 0% packet loss,'); then - printf "Are you sure you want to back everything up to %s?\nIs the root password set and all storage mounted?\nDo you have time for this backup to complete?\nEnter YES in all capitals to continue..." "$1" +# Default the host to the Aether host in whatever domain this host lives in. +host="$1" +if [ -z "$host" ]; then + host=Aether +fi + +# Default the path to / +path="$2" +if [ -z "$path" ]; then + path='/' +fi + +# Try to contract the host +if (nmap --open -p 22 "$host" | grep -E '22/tcp\s+open' &>/dev/null); then + + # Confirm with the user + printf "Are you sure you want to back everything up to %s?\n* Is the root password set and backup volume mounted on '/mnt'?\n* Do you have time for this backup to complete?\n* Does \`ssh-keyscan -D localhost\` on the server match this? `ssh-keyscan -D "$host" 2>/dev/null | grep 4\ 2`\nEnter YES in all capitals to continue..." "$host" + read answer if [ "$answer" != "YES" ]; then echo User did not confirm. exit 1; else - rsync -aAXv --delete --exclude={"/dev/*","/proc/*","/sys/*","/tmp/*","/run/*","/mnt/*","/media/*","/lost+found"} / root@"$1":/mnt/ + rsync -aAXv --delete --exclude={"/dev/*","/proc/*","/sys/*","/tmp/*","/run/*","/mnt/*","/media/*","/lost+found"} "$path" root@"$host":/mnt"$path" status="$?" echo rsync exited with status $status exit $status diff --git a/tests/test_units.py b/tests/test_units.py index 40b35ef..7aad671 100644 --- a/tests/test_units.py +++ b/tests/test_units.py @@ -5,7 +5,7 @@ import subprocess # TODO Still need to devise a testing strategy (https://foundation.aninix.net/AniNIX/Aether/issues/1) def test_aether(): print(os.getcwd()) - fh = os.popen("echo bye | sftp -o IdentityFile=./aether aether@aninix.net", mode='r', buffering=-1) + fh = os.popen("echo bye | timeout 3 sftp -o IdentityFile=./aether aether@aninix.net", mode='r', buffering=-1) output = fh.read() retcode = fh.close() - assert retcode == None and CheckOutput(output) + assert retcode == None