{"id":19357893,"url":"https://github.com/thexhr/openbsd-timemachine","last_synced_at":"2025-04-23T11:30:49.635Z","repository":{"id":71259451,"uuid":"497288983","full_name":"thexhr/openbsd-timemachine","owner":"thexhr","description":"Timemachine like backups on OpenBSD","archived":false,"fork":false,"pushed_at":"2024-06-13T17:57:32.000Z","size":17,"stargazers_count":23,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-06-13T20:52:10.626Z","etag":null,"topics":["backup","openbsd","rsnapshot","rsync-wrapper","shell","shell-script","timemachine"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thexhr.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-05-28T10:46:37.000Z","updated_at":"2024-06-13T17:57:35.000Z","dependencies_parsed_at":null,"dependency_job_id":"d90fff4a-b2e2-4134-bb6f-ea35586722e4","html_url":"https://github.com/thexhr/openbsd-timemachine","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thexhr%2Fopenbsd-timemachine","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thexhr%2Fopenbsd-timemachine/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thexhr%2Fopenbsd-timemachine/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thexhr%2Fopenbsd-timemachine/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thexhr","download_url":"https://codeload.github.com/thexhr/openbsd-timemachine/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223922099,"owners_count":17225636,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["backup","openbsd","rsnapshot","rsync-wrapper","shell","shell-script","timemachine"],"created_at":"2024-11-10T07:09:34.149Z","updated_at":"2024-11-10T07:09:34.611Z","avatar_url":"https://github.com/thexhr.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Time Machine like Backups on OpenBSD\n\n[Time Machine](https://en.wikipedia.org/wiki/Time_Machine_(macOS)) is a backup software developed by Apple and a part of macOS.  It allows easy and foolproof backups.  In a nutshell, it creates incremental backups on a storage medium of your choice and you can access the data either with a graphical client or directly via file system tools.  I especially like that you only have to plug in an external USB drive which is immediately recognized, the backup starts and the drive is unmounted as soon as the backup is done. Since Time Machine is Apple only and I use OpenBSD on all my personal machines, I decided to write my own Time Machine like solution.\n\n## Goals of my Solution\n\n* Automatic, incremental backups as soon as an external USB device is connected\n* Automatic unmounting as soon as the backup is finished\n* Data is fully encrypted\n* No proprietary backup format, just plain files on disk\n\nTurns out, I can solve these goals easily with base software and one program from ports.\n\n## Prepare the external Storage\n\nAt first, we need to manually format the disk and create an encrypted file system on top.  I am using a simple external USB hard drive here.  Plug in the disk and find the correct device name by looking at the dmesg output:\n\n\n```\numass0 at uhub0 port 2 configuration 1 interface 0 \"Kingston DataTraveler 2.0\" rev 2.00/1.00 addr 2\numass0: using SCSI over Bulk-Only\nscsibus4 at umass0: 2 targets, initiator 0\nsd2 at scsibus4 targ 1 lun 0: \u003cKingston, DataTraveler 2.0, PMAP\u003e removable serial.09511607BA7195A60256\nsd2: 7640MB, 512 bytes/sector, 15646720 sectors\n```\n\nIn this example it's `sd2`.  Now we need to format the disk and create a RAID disklabel for an encrypted file system on top of it.\n\n```\n# fdisk -iy sd2\nWriting MBR at offset 0.\n\n# disklabel -E sd2\nLabel editor (enter '?' for help at any prompt)\nsd2\u003e a a\noffset: [64]\nsize: [15631181]\nFS type: [4.2BSD] RAID\nsd2*\u003e w\nsd2\u003e q\n```\nUpon completion you should see a correct disklabel on the disk.\n\n```\n# disklabel sd2\n# /dev/rsd2c:\ntype: SCSI\ndisk: SCSI disk\nlabel: DataTraveler 2.0\nduid: f5a87db156d32c6f      \u003c- the value here will be used later\nflags:\nbytes/sector: 512\nsectors/track: 63\ntracks/cylinder: 255\nsectors/cylinder: 16065\ncylinders: 973\ntotal sectors: 15646720\nboundstart: 64\nboundend: 15631245\ndrivedata: 0\n\n16 partitions:\n#                size           offset  fstype [fsize bsize   cpg]\n  a:         15631181               64    RAID\n  c:         15646720                0  unused\n```\n\nSince the disk is later controlled by a script we cannot use a passphrase for encryption, we need to store the decryption password in a file.  Use the tool of your choice to generate a strong password and store it in a file. To match the passphrase and the disk, name the file after the disks `duid` (can been seen in disklabel's output above).  As last step, set the file's permission to 600 so that only `root` can access it.  Otherwise, bioctl complains about wrong permissions.\n\nSave the file under `/root`.  Further, write the generated password somewhere down in case you need to access your backup disk without (!) having access to your machine!  You could print it on a piece of paper and store it somewhere safe.\n\n```\n# openssl rand -hex 60 \u003e /root/f5a87db156d32c6f.pw\n\n# cat /root/f5a87db156d32c6f.pw\n7c52430bf63c40f4f84b1a6bb0157c1d72181cca8d2bf9e296f529a8b0ceaecb058ae99508d1d3b4...\n\n# chmod 600 /root/f5a87db156d32c6f.pw\n# chown root:wheel /root/f5a87db156d32c6f.pw\n```\n\nNow we create an encrypted diskabel within the previous one using the file's content as passphrase:\n\n```\n# bioctl -c C -r auto -p /root/f5a87db156d32c6f.pw -l /dev/sd2a softraid0\nsoftraid0: CRYPTO volume attached as sd3\n\n# disklabel -E sd3\nLabel editor (enter '?' for help at any prompt)\nsd3\u003e a i\noffset: [0]\nsize: [15630653]\nFS type: [4.2BSD]\nsd3*\u003e w\nsd3\u003e q\nNo label changes.\n\n# disklabel sd3\n# /dev/rsd3c:\ntype: SCSI\ndisk: SCSI disk\nlabel: SR CRYPTO\nduid: 4be3be137f4ba195\nflags:\nbytes/sector: 512\nsectors/track: 63\ntracks/cylinder: 255\nsectors/cylinder: 16065\ncylinders: 972\ntotal sectors: 15630653\nboundstart: 0\nboundend: 15630653\ndrivedata: 0\n\n16 partitions:\n#                size           offset  fstype [fsize bsize   cpg]\n  c:         15630653                0  unused\n  i:         15630624                0  4.2BSD   2048 16384 12960\n```\n\nTo double check that everything works as designed, detach and re-attach the disk:\n\n```\n# bioctl -d sd3\n# bioctl -c C -p /root/f5a87db156d32c6f.pw -l /dev/sd2a softraid0\nsoftraid0: CRYPTO volume attached as sd3\n```\n\nAt last, we create a file system where the backups will be stored.  Using the -O 2 option we force `newfs` to create a FFS2 file system.\n\n```\n# newfs -O 2 /dev/rsd3i\n/dev/rsd3i: 7632.1MB in 15630624 sectors of 512 bytes\n38 cylinder groups of 202.50MB, 12960 blocks, 25920 inodes each\nsuper-block backups (for fsck -b #) at:\n 160, 414880, 829600, [...]\n# mount /dev/sd3i /mnt/\n# ls -l /mnt/\n```\n\nThe external disk is now ready to be used.\n\n## Install the script\n\nDownload the script or clone the repository to a location of your choice and then install the script.  For security reasons, it lives in the /root directory.  Perform the following steps as root:\n\n```\n# git clone https://github.com/thexhr/openbsd-timemachine\n# cd openbsd-timemachine\n# make install\n```\n\n## Recognize the disk upon connection\n\nNow, we make sure that the disk is recognized by the system as soon as it's connected.  This can be easily done with [hotplugd](https://man.openbsd.org/hotplugd) and a small script you see in the following.  To identify the disk we look at the disklabel of each attached disk and run a script as soon as it's connected.\n\n```\n# cat /etc/hotplug/attach\n#!/bin/sh\n\nDEVCLASS=$1\nDEVNAME=$2\n\ncase $DEVCLASS in\n\t2)\n\t# disk devices\n\tduid=`/sbin/disklabel $DEVNAME 2\u003e\u00261 | sed -n '/^duid: /s/^duid: //p'`\n\tcase $duid in\n\t\tf5a87db156d32c6f)\n\t\t# Example USB stick\n\t\tlogger -i \"Example USB stick attached\"\n\t\tsh /root/openbsd-timemachine-backup.sh f5a87db156d32c6f 4be3be137f4ba195 /root/f5a87db156d32c6f.pw\n\t\t;;\n\tesac\nesac\n```\n\nSo what does the script above? It is called by hotplugd every time a device is attached.  It checks if a disk is attached (DEVCLASS is 2) and then get the disk's duid from disklabel.  If the duid matches the on from the backup disk (f5a87db156d32c6f in our case), it starts a script called `/root/openbsd-timemachine-backup.sh`.  The script gets three parameters:\n\n* The duid of the just connected, still encrypted USB disk\n* The duid of the decrypted disklabel\n* Full path to the file with the passphrase\n\nIt also logs some information to syslog to make you aware that a backup disk is connected.\n\n## Install and configure rsnapshot\n\n[rsnapshot](https://rsnapshot.org) is used for backing up the data. According to the website \"rsnapshot is a file system snapshot utility based on rsync. rsnapshot makes it easy to make periodic snapshots of local machines, and remote machines over ssh. The code makes extensive use of hard links whenever possible, to greatly reduce the disk space required.\" So, exactly what we're looking after.\n\nInstall it from ports:\n\n```\n# pkg_add rsnapshot\n```\n\nThe simplest way to configure it, is to copy the example config from /usr/local/share/examples/rsnapshot/rsnapshot.conf.default to /etc/rsnapshot.conf and adapt it as needed.  The things you need to configure to make it work with the script below are as follows:\n\n```\n# All snapshots will be stored under this root directory.\n#\n-snapshot_root  /.snapshots/\n+snapshot_root  /backup/\n\n# LOCALHOST\n-#backup\t/home/\tlocalhost/\n+backup \t/\t\tlocalhost/\n```\n\nKeep the Greek letter names (alpha, beta, ...) for the backup levels.  Depending on your available backup disk size you might want to tune the number of snapshots to be retained. To make sure that rsnapshot works as expected mount your backup drive to /backup and run it once.  Check for errors and resolve them, if needed.\n\n```\n# mkdir /backup\n# rsnapshot -c /etc/rsnapshot.conf alpha\n```\n\nIf rsnapshot works as expected we can now configure the script that runs it automatically.\n\n## Connect the disk to start a Backup\n\nThe [script](openbsd-timemachine-backup.sh) is quite simple and just decrypts the disk, mounts it and runs rsnapshot to create an incremental backup.\n\nOnce you connect the disk you should see a backup job running and similar output to the following in `/var/log/messages` (timestamps cut). Upon the first call, a counter is written to the backup disk.  Every 8th run, a rsnapshot gamma backup is done, every 4th run a beta backup, and an alpha backup on all other runs.\n\n```\nsd2 at scsibus4 targ 1 lun 0: \u003cWD, Elements 25A1, 1018\u003e serial.105825a214463442\nsd2: 2861556MB, 512 bytes/sector, 5860466688 sectors\nroot[28211]: 2TB Backup USB disk attached\nopenbsd-timemachine-backup.sh: Backup disk successfully bio-attached\nsd3 at scsibus3 targ 2 lun 0: \u003cOPENBSD, SR CRYPTO, 006\u003e\nsd3: 2097095MB, 512 bytes/sector, 4294852016 sectors\nroot: openbsd-timemachine-backup.sh: Backup disk mounted successfully to /backup\nroot: openbsd-timemachine-backup.sh: Iteration 54, doing an alpha backup\nroot: openbsd-timemachine-backup.sh: /backup successfully unmounted\nsd3 detached\nroot: openbsd-timemachine-backup.sh: disk successfully bio-detached\n```\n\n### Can I modify the script?\n\nDon't do it.  Really, don't.  If you really want to change something, double check the following points:\n\n* To avoid nested mounts, the script uses /backup as mount point for the external device.  If you prefer another location you have to change the MNTPOIN variable at the beginning of the script and don't forget to change rsnapshot's config as well.\n* As seen above, I created the outer partition as sdXa (note the small letter 'a') and the inner partition as sdXi (note the small letter 'i').  If you chose a different partition in disklabel you have to change the bioctl and mount commands.\n* If you chose different names for rsnapshot's backup levels (so others that alpha, beta, ...) you have to modify the script accordingly.\n\n\n## The fine Print\n\nOf course, this script comes without warranty.  Double check that everything works correctly and always have a second backup ready.\n\n## License\nThe script was written by Matthias Schmidt and is licensed under the ISC license.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthexhr%2Fopenbsd-timemachine","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthexhr%2Fopenbsd-timemachine","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthexhr%2Fopenbsd-timemachine/lists"}