In this post, I am setting up a Time Capsule and Backintime server. I am using a Raspberry Pi that has Ubuntu installed, with a USB disk that has been configured into a ZFS pool.
Setting up backup users
You are going to have to create users for each of the services/users that will be connecting to the server. You want to keep files and access as isolated as possible. As in a given user shouldn’t have any visibility or notion of other users’ backups. We are also creating accounts that can’t login into the system for Time Machine, only authenticate.
Check if there is an entry for nologin in:
$ cat /etc/shells
If there is no entry add it:
# vim /etc/shells
# /etc/shells: valid login shells /bin/sh /bin/bash /bin/rbash /bin/dash /usr/bin/tmux /usr/sbin/nologin
Create a generic user for the backups, or dedicated accounts for each user to increase security:
Generic user example:
# useradd -s /usr/sbin/nologin timemachine # passwd timemachine
Dedicated user example:
# useradd -s /usr/sbin/nologin timemachine_john # passwd timemachine_john
Note that useradd doesn’t create a home
If required, the default shell can be changed with:
# usermod -s /usr/sbin/nologin timemachine_john
Setting up backup user groups
If more than one system is going to be backed up it is advisable to use different accounts for each.
It is possible to isolate users by assigning them individual datasets, but that might create storage silos.
An alternative is to create individual users that belong to the same backup group. The backup group can access the backintime dataset, but not each other’s data.
Create the group.
# addgroup backupusers
Assign main group and secondary group (the secondary group would be the shared one).
# usermod -g timemachine_john -G backupusers timemachine_john
Although not required, you could force the UID and GID to be a specific one.
# usermod -u 1012 timemachine # groupmod -g 1012 timemachine
Install netatalk from the repositories.
# apt install netatalk
Allow access to all the appropriate accounts to the directory where the backups are going to be written to:
# chown :timemachine_john /backups/timecapsule/ # chmod 775 /backups/timecapsule/
Edit the settings of the netatalk service so that that share can be seen with the name of your choice and work as a Time Capsule server.
# vim /etc/netatalk/AppleVolumes.default
Enter the following:
/backups/timecapsule "pi-capsule" options:tm
Note that you can give the capsule a name with spaces above.
Restart the service:
# systemctl restart netatalk
Check that netatalk has been installed correctly:
# afpd -V afpd 3.1.12 - Apple Filing Protocol (AFP) daemon of Netatalk [...] afpd has been compiled with support for these features: AFP versions: 2.2 3.0 3.1 3.2 3.3 3.4 CNID backends: dbd last tdb Zeroconf support: Avahi TCP wrappers support: Yes Quota support: Yes Admin group support: Yes Valid shell checks: Yes cracklib support: No EA support: ad | sys ACL support: Yes LDAP support: Yes D-Bus support: Yes Spotlight support: Yes DTrace probes: Yes afp.conf: /etc/netatalk/afp.conf extmap.conf: /etc/netatalk/extmap.conf state directory: /var/lib/netatalk/ afp_signature.conf: /var/lib/netatalk/afp_signature.conf afp_voluuid.conf: /var/lib/netatalk/afp_voluuid.conf UAM search path: /usr/lib/netatalk// Server messages path: /var/lib/netatalk/msg/
# vim /etc/nsswitch.conf
Change this line:
hosts: files mdns4_minimal [NOTFOUND=return] dns
hosts: files mdns4_minimal [NOTFOUND=return] dns mdns4 mdns
Note that if you are running Netatalk 3.1.11 or above it is not necessary any more to create the /etc/avahi/services/afpd.service. Using this file will cause an error.
If you are running an older version go ahead, otherwise jump to the next section.
Create /etc/avahi/services/afpd.service as root
# vim /etc/avahi/services/afpd.service
and fill it up with:
<?xml version="1.0" standalone='no'?><!--*-nxml-*--> <!DOCTYPE service-group SYSTEM "avahi-service.dtd"> <service-group> <name replace-wildcards="yes">%h</name> <service> <type>_afpovertcp._tcp</type> <port>548</port> </service> <service> <type>_device-info._tcp</type> <port>0</port> <txt-record>model=TimeCapsule</txt-record> </service> </service-group>
Configure the AFP service
Edit the configuration file.
# vim /etc/netatalk/afp.conf
[Global] ; Global server settings mimic model = TimeCapsule6,106 [pi-capsule] path = /backups/timecapsule time machine = yes
Check configuration and reload if needed:
# systemctl status avahi-daemon [restart if necessary] # systemctl restart netatalk [Make the service automatically start] # systemctl enable netatalk.service
If you go to your Mac’s Time Machine preferences the new volume will be available and you can start using it.
Some notes of things to check from the server side (Time Capsule server):
If you have disabled passwords and are only using keys, you will need to temporarily change the security settings to allow Backintime to exchange keys.
On the remote system/Pi/server:
# vim /etc/ssh/sshd_config
# systemctl restart ssh
Backintime uses SSH, so the user accounts need to be allowed to login. Therefore the default login shell needs to reflect this.
If not created already, assign the user a home directory. Finally, allow the user to read and write the folder containing the backups.
# usermod -s /usr/bin/bash backintime_john # mkdir /home/backintime_john # chown backintime_tuxedo:backintime_john /home/backintime_john/ # usermod -d /home/backintime_john/ backintime_john # chown :backupusers /backups/backintime/ # chmod 775 /backups/backintime/
Permissions for some of the subfolders might be required in multi-user configuration after the first backup:
# chown :backupusers /backups/backintime*/backintime # chmod 770 /backups/backintime*/backintime/system1/ # chmod 770 /backups/backintime*/backintime/laptop2/
To simplify things these are the roles:
[Local system] The client machine that is running Backintime and that you want to backup your data from. [Remote system] The SSH server that has the storage where your backup is going to be stored.
From the local system account you want to run backintime (either your user or root, depending on how you run Backintime) SSH into the remote system. In my case, a Raspberry Pi.
# ssh email@example.com
After logging in check the host key.
$ ssh-keygen -l -f /etc/ssh/ssh_host_ecdsa_key.pub 256 SHA256:KjzU6aGqH6tXri/K87xz3H+cP35PMT7n+Ob6MIaBZb0 root@pi-capsule (ECDSA)
You can then log out from the remote machine.
From the local account, you want to run Backintime from generate a new SSH key pair.
And then copy the public key to the Pi.
# ssh-copy-id -i ~/.ssh/id_rsa.pub backintime_david@pi-capsule [...] ECDSA key fingerprint is SHA256:KjzU6aGqH6tXri/K87xz3H+cP35PMT7n+Ob6MIaBZb0. [...] Number of key(s) added: 1 [...]
Note that the fingerprint is the same as the one displayed in the previous step.
Configure Backintime profile
You can now configure the SSH profile from Backintime and make the first run.
In the General tab:
Mode: SSH SSH Settings Host: pi-capsule Port: 22 User: backintime_david Path: /backups/backintime_david Cipher: [Leave as default] Private Key:/root/.ssh/id_rsa Password SSH private key:[empty in most cases] Enable Cache Password Advanced Host: tuxedo User: root Profile: 2 Schedule [Select appropriate setting after testing]
Include /home /etc/ /boot /root /steam /opt /usr /var
Exclude (example) /steam/steamapps/downloading /var/cache /vm/kvm_images/__security/Security_TryHackMe*.qcow2
Auto-remove Older than 10 years If free space is less than 50GiB If free inodes is less than 2% Smart remove Run in background on remote Host Keep last 14 days (7 days) 21 days (14 days) 8 weeks (6 weeks) 36 months (14 months) Don't remove named snapshots
Options Enable notifications Backup replaced files on restore Continue on errors (keep incomplete snapshots) Log level: Changes & Errors
After the first run has completed you can check which is the best performing cipher from the CLI.
# backintime benchmark-cipher --profile-id 2
After a few rounds, aes192-ctr came out as the best performing cipher for me.
If you changed the SSH configuration at the beginning, after setting everything up, remember to secure SSH again on the server/remote system.
# vim /etc/ssh/sshd_config
# systemctl restart ssh
Restoring restrictions to backup users
The login account is required for Backintime to be able to run rsync. It is worth doing a bit more research on how to harden/limit these accounts.
Some examples of some issues and some troubleshooting steps you can apply.
Time Capsule can’t be reached / firewall settings
Make sure the server is allowing AFP connections from the Mac client.
# ufw allow proto tcp from CLIENT_IP to PI_CAPSULE_IP port 548
Time Capsule – Configuring Time Machine backups via the network on a macOS VM
The destination needs to be configured manually.
Mount the AFP/Time Capsule mount via the Finder.
In the CLI configure the destination:
# tmutil setdestination -a /Volumes/pi-capsule
The backups can then be started from the GUI.
You can get information about the current configured destinations via the CLI.
# tmutil destinationinfo ==================================== Name : pi-capsule Kind : Network Mount Point : /Volumes/pi-capsule ID : 7B648734-9BFC-417F-B5A1-F31B8AD52F4B
Time Capsule – Checking backup status
# tmutil currentphase
# tmutil status
ZFS stalling on a Raspberry Pi
Check the recordsize property. Reduce it to the default 128 kiB.
Reduce ARC size to reduce the amount of memory consumed/reserved for ZFS.
Understanding rsync logs
The logs indicate the type of change rsync is seeing. A reference is available here:
XYcstpoguax path/to/file ||||||||||| ||||||||||╰- x: The extended attribute information changed |||||||||╰-- a: The ACL information changed ||||||||╰--- u: The u slot is reserved for future use |||||||╰---- g: Group is different ||||||╰----- o: Owner is different |||||╰------ p: Permission are different ||||╰------- t: Modification time is different |||╰-------- s: Size is different ||╰--------- c: Different checksum (for regular files), or || changed value (for symlinks, devices, and special files) |╰---------- the file type: | f: for a file, | d: for a directory, | L: for a symlink, | D: for a device, | S: for a special file (e.g. named sockets and fifos) ╰----------- the type of update being done:: <: file is being transferred to the remote host (sent) >: file is being transferred to the local host (received) c: local change/creation for the item, such as: - the creation of a directory - the changing of a symlink, - etc. h: the item is a hard link to another item (requires --hard-links). .: the item is not being updated (though it might have attributes that are being modified) *: means that the rest of the itemized-output area contains a message (e.g. "deleting")
Some example output:
>f+++++++++ some/dir/new-file.txt .f....og..x some/dir/existing-file-with-changed-owner-and-group.txt .f........x some/dir/existing-file-with-changed-unnamed-attribute.txt >f...p....x some/dir/existing-file-with-changed-permissions.txt >f..t..g..x some/dir/existing-file-with-changed-time-and-group.txt >f.s......x some/dir/existing-file-with-changed-size.txt >f.st.....x some/dir/existing-file-with-changed-size-and-time-stamp.txt cd+++++++++ some/dir/new-directory/ .d....og... some/dir/existing-directory-with-changed-owner-and-group/ .d..t...... some/dir/existing-directory-with-different-time-stamp/