Creating FIDO2 SSH keys using ssh-keygen

$ ssh-keygen -t ecdsa-sk -f ~/.ssh/id_ecdsa_sk
  • -t ecdsa-sk specifies the key type to generate. Alternatively, you can generate Ed25519 keys using -t ed25519-sk.
  • -f ~/.ssh/id_ecdsa_sk specify the output path for the newly generated key.

You can provide a passphrase for your key if you would like to do so. Unlike normal ssh keys, the private key is not that sensitive, as it is useless without the physical security key itself.

ed25519-sk vs ecdsa-sk

Newer YubiKeys (firmware >=5.2.3) and some other FIDO2 keys, support Ed25519 keys. Ed25519 have some advantages over the common ECDSA keys in several aspects:

  • Ed25519 is based on the Curve25519 vs NIST P-256 used for ecdsa-sk. Curve25519 is generally regarded as faster and safer than NIST P-256, see SafeCurves. Furthermore, the underlying signature algorithm (Schnorr vs DSA) is slightly faster for Ed25519
  • EdDSA in general, and Ed25519 in particular, uses deterministic nonce versus random nonce used by ECDSA. This means that ECDSA is prune to catastrophic entropy failure (see the famous fail0verflow PS3 hack as an example). Assuming your key has access to high entropy randomness, that shouldn’t be a problem. However, that assumption might turn out false, like in the case of the reduced initial randomness in the Yubikey FIPS Series.

In the bottom line, if you have access to a key that supports ed25519-sk then it’s preferable to use it. If you don’t, that’s not something to worry about to much. There are probably weaker points in your threat model anyway.

If your FIDO2 key doesn’t support ed25519-sk you will get the following error when trying to generate a key:

Key enrollment failed: requested feature not supported

Moving keys to a new computer

If you want to use the keys on a new computer, you will have to copy over the private key file that you generated. That will normally be ~/.ssh/id_ecdsa_sk or ~/.ssh/id_ed25519_sk, depending on the type of key you generated.

Alternatively, you can generate resident keys which are completely stored on the YubiKey. To generate resident keys, append the some flag to your ssh-keygen command. Example:

$ ssh-keygen -t ecdsa-sk -O resident

To import the keys to a new device, use the -K option:

$ ssh-keygen -K

This will download all the keys (public and private) from the YubiKey to the current directory. There is no need to manually transfer any key files.

YubiKey Series 5 devices can hold up to 25 resident keys.

Unlock LUKS volume with a YubiKey

Update: The dracut configuration has been updated and now udev consistently recognizes the YubiKey in the initramfs.

Unlocking LUKS encrypted drives with a YubiKey has been supported since systemd 248. In Debian, systemd>=250 is required, as the feature has not been enabled in prior versions. This tutorial is geared towards Yubikeys, but it should work with slight modifications with any other FIDO2 token.

YubiKey series 5 and later should support the hmac-secret extension. You can make sure your Yubikey supports the needed hmac-secret extension by querying it with ykman:

$ ykman --diagnose 2>&1 | grep hmac-secret

Backup your LUKS header

In case you mess anything up, you would need a backup of your LUKS header. Remember to save your backup to some external storage, so you can actually access it if anything goes sideways.

# cryptsetup luksHeaderBackup /dev/nvme0n1p3 --header-backup-file /media/guyru/E474-2D80/luks_backup.bin

Set FIDO2 PIN

We would like to set a FIDO2 PIN for the Yubikey, so unlocking the encrypted drive would require both the physical Yubikey and the PIN. You can set the PIN using:

$ ykman fido access change-pin

Enroll the Yubikey

Start by verifying that systemd-cryptenroll can see and can use your YubiKey:

$ systemd-cryptenroll --fido2-device=list
PATH         MANUFACTURER PRODUCT
/dev/hidraw0 Yubico       YubiKey FIDO+CCID

Now, enroll the Yubikey, replacing /dev/nvme0n1p3 with the block device of the LUKS encrypted drive.

$ sudo systemd-cryptenroll /dev/nvme0n1p3 --fido2-device=auto  --fido2-with-client-pin=yes
🔐 Please enter current passphrase for disk /dev/nvme0n1p3: (no echo)
Initializing FIDO2 credential on security token.
👆 (Hint: This might require confirmation of user presence on security token.)
🔐 Please enter security token PIN: (no echo)
Generating secret key on FIDO2 security token.
👆 In order to allow secret key generation, please confirm presence on security token.
New FIDO2 token enrolled as key slot 0.

Modify /etc/crypttab

We need to modify /etc/crypttab in order to tell cryptsetup to unlock the device using the YubiKey. Add fido2-device=auto in the options field of the crypttab entry for your device. For example:

nvme0n1p3_crypt UUID=307a6bef-5599-4963-8ce0-d9e999026c1a none luks,discard,fido2-device=auto

Switch to dracut

Debian’s default initramfs generator, update-initramfs of the initramfs-tools is using the old cryptsetup for mounting encrypted drives. However, cryptsetup doesn’t recognize the fido2-device option. Running update-initramfs will fail with the following error:

$ sudo update-initramfs -u
update-initramfs: Generating /boot/initrd.img-5.15.0-3-amd64
cryptsetup: WARNING: nvme0n1p3_crypt: ignoring unknown option 'fido2-device'

This is unfortunate. The simplest solution is to switch to dracut, a more modern initramfs generator, which among other things relies on systemd to activate encrypted volumes. This solves the issue of the unknown fido2-device.

Before installing dracut, I would highly recommend creating a copy of the existing initramfs in the boot partition in case something goes wrong.

$ sudo apt install dracut

Dracut includes systemd-cryptsetup by default. systemd-cryptsetup depends on libfido for unlocking devices using FIDO2 tokens. At least in Debian, systemd-cryptsetup dynamically loads libfido2.so (as opposed to being dynamically linked), which causes dracut not to have libfido2.so in the initramfs. This causes systemd-cryptsetup to issue the following error upon boot:

FIDO2 tokens not supported on this build. 

We fix it by manually adding libfido2.so to the initramfs. Of course, we also need to include libfido2’s dependencies as well. Dracut has a mechanism for automatically adding dependencies for executables, but it doesn’t work on libraries. As a workaround, instead of adding libfido2 directly, we will add an executable that depends on libfido2, which will add libfido2 and its dependencies to the initramfs. We will usefido2-token from the fido2-tools package for this trick.

$ sudo apt install fido2-tools
$ cat << EOF | sudo tee /etc/dracut.conf.d/11-fido2.conf
## Spaces in the quotes are critical.
# install_optional_items+=" /usr/lib/x86_64-linux-gnu/libfido2.so.* "

## Ugly workround because the line above doesn't fetch
## dependencies of libfido2.so
install_items+=" /usr/bin/fido2-token "

# Required detecting the fido2 key
install_items+=" /usr/lib/udev/rules.d/60-fido-id.rules /usr/lib/udev/fido_id "
EOF

Now, recreate the initramfs images:

$ sudo dracut -f

Last remarks

At this point, we are done. Reboot you’re machine and it will prompt you for your YubiKey and allow you to unlock your LUKS encrypted root patition with it. If you don’t have your YubiKey, it will give the following prompt:

Security token not present for unlocking volume root (nvme0n1p3_crypt), please plug it in.

After around 30 seconds, it would time out and display the following message:

Timed out waiting for security device, aborting security device based authentication attempt.

Afterwards, it would allow you to unlock the partition using a password (or a recovery key).

In case you run into any trouble, append rd.break=initqueue to the kernel command line, and dracut will enter a shell before attempting to mount the partitions. You can manually mount the drive using the following command:

# /usr/lib/systemd/systemd-cryptsetup attach root /dev/nvme0n1p3

Exit the emergency shell, and the system will continue its normal boot.

Replacing PulseAudio with PipeWire 0.3.30

Replacing PulseAudio with Pipewire became much simpler recently with PipeWire 0.3.30 and requires less configuration. I’m going to go through the updated routine. You can read the original post for more explanations.

The new version is still only available in experimental as of today.

$ sudo apt install -t experimental pipewire-pulse pipewire-audio-client-libraries libspa-0.2-bluetooth

The pipewire-pulse package takes care of most of the configuration that was previously needed, like touching with-pulseaudio or manually creating the systemd service files.

$ systemctl --user daemon-reload
$ systemctl --user disable pulseaudio.socket pulseaudio.service
$ systemctl --user stop pulseaudio.socket pulseaudio.service
$ systemctl --user mask pulseaudio.service pulseaudio.socket
$ systemctl --user enable --now pipewire pipewire-pulse pipewire-media-session

Don’t remove the PulseAudio packages yet. While not being used, some packages still depend specifically on PulseAudio and might break. See the original post for more details.

Enable mSBC and SBC XQ

One of the main advantages of PipeWire is proper support for better sounding bluetooth audio profiles, and specifically mSBC and SBC XQ. Copy /usr/share/pipewire/media-session.d/bluez-monitor.conf to ~/.config/pipewire/media-session.d/bluez-monitor.conf (or to /etc/pipewire/media-session.d/bluez-monitor.conf) and in the properties section add the following lines:

bluez5.msbc-support   = true
bluez5.sbc-xq-support = true

Replacing PulseAudio with PipeWire

PipeWire is a multimedia server, best known for it’s video support in Wayland. It also provides an audio server which can replace PulseAudio. The appeal, for me at least, to switch over from PulseAudio to PipeWire stems from PipeWire’s better support of bluetooth audio, and especially support for modern A2DP codecs such as AptX, AptX HD and LDAC.

Starting with PipeWire 0.3.20 introduced native mSBC support. This profile support mSBC codec versus CSVD supported by the older HSP/HFP profiles. The difference is significant, as the CSVD only supported narrow band speech (NBS, 8kHz) compared with mSBC support for wide band speech (WBS, 16kHz). That is the difference between 90’s era call quality sound and modern call quality sound.

Update: PipeWire-0.3.30 made the replacement process simpler. See my updated post.

Installing PipeWire 0.3.23

As of writing this post, Debian Unstable only has PipeWire 0.3.19. We are going to install PipeWire from the experimental repo so we get the PipeWire 0.3.23 with the support for mSBC.

We start by enabling the experimental repo

$ sudo apt-add-repository "deb http://deb.debian.org/debian experimental main
$ sudo apt update

Install PipeWire from experimental:

$ sudo apt install -t experimental pipewire-audio-client-libraries libspa-0.2-bluetooth

(pipewire-audio-client-libraries will pull pipewire itself as a dependency)

Substituting PipeWire for PulseAudio

These instructions are based on the ones from Debian Wiki, Arch Wiki and Gentoo Wiki. Create the file

$ sudo touch /etc/pipewire/media-session.d/with-pulseaudio

It will instruct PipeWire to handle Bluetooth audio devices.

Copy the pipewire-pulse systemd service:

$ sudo cp /usr/share/doc/pipewire/examples/systemd/user/pipewire-pulse.{service,socket} /etc/systemd/user

Disable PulseAudio services and enable the PipeWire ones

$ systemctl --user disable pulseaudio.socket pulseaudio.service
$ systemctl --user stop pulseaudio.socket pulseaudio.service
$ systemctl --user enable pipewire pipewire-pulse
$ systemctl --user start pipewire pipewire-pulse

If everything worked well pactl info should report Server Name: PulseAudio (on PipeWire 0.3.23):

$ pactl info | grep "Server Name"
Server Name: PulseAudio (on PipeWire 0.3.23)

If not, you might need to restart (PulseAudio tends to be rather persistent). In case PulseAudio still doesn’t play nicely, you should mask it:

$ systemctl --user mask pulseaudio.service pulseaudio.socket
$ systemctl --user stop pulseaudio.service pulseaudio.socket

Removing PulseAudio completely is not a good move at this point in time. Some packages depend on it, although they could work with PipeWire just as well. For example, when I remvoed PipeWire libcanberra-pulse got removed as well which caused system notification sounds to break. Alternatively you could try to replace the PulseAudio package with a dummy using equivs but that seems like more effort than keeping the package.

Enabling mSBC and SBC XQ

Edit /etc/pipewire/media-session.d/bluez-monitor.conf and uncomment the following lines:

bluez5.msbc-support   = true
bluez5.sbc-xq-support = true

This will enable both mSBC and SBC XQ.

You can test that you’re headset is connected via mSBC using pw-cli info:

$ guyru@gdebian3:~$ pw-cli info all | grep bluez
info: unsupported type PipeWire:Interface:Profiler
info: unsupported type PipeWire:Interface:Metadata
info: unsupported type PipeWire:Interface:Metadata
*		device.api = "bluez5"
*		device.name = "bluez_card.94_DB_56_AC_36_52"
*		api.bluez5.path = "/org/bluez/hci0/dev_94_DB_56_AC_36_52"
*		api.bluez5.address = "94:DB:56:AC:36:52"
*		api.bluez5.device = ""
*		api.bluez5.class = "0x240404"
*		api.bluez5.transport = ""
*		api.bluez5.profile = "headset-head-unit"
*		api.bluez5.codec = "mSBC"
*		api.bluez5.address = "94:DB:56:AC:36:52"
*		node.name = "bluez_input.94_DB_56_AC_36_52.headset-head-unit"
*		factory.name = "api.bluez5.sco.source"
*		device.api = "bluez5"
*		api.bluez5.transport = ""
*		api.bluez5.profile = "headset-head-unit"
*		api.bluez5.codec = "mSBC"
*		api.bluez5.address = "94:DB:56:AC:36:52"
*		node.name = "bluez_output.94_DB_56_AC_36_52.headset-head-unit"
*		factory.name = "api.bluez5.sco.sink"
*		device.api = "bluez5"

In case mSBC is not supported you’ll see api.bluez5.codec = "CSVD" (and you’lll probably hear the difference).

Errors

Problem: Connecting to bluetooth headset fails, and the following error appears in journalctl:

bluetoothd[41893]: src/service.c:btd_service_connect() a2dp-sink profile connect failed for 94:DB:56:AC:36:52: Protocol not available

Solution: You’re missing the libspa-0.2-bluetooth package. Install it and restart PipeWire:

$ sudo apt install -t experimental libspa-0.2-bluetooth
$ systemctl --user restart pipewire pipewire-pulse

Problem: ALSA programs fail with the following error:

ALSA lib pcm_dmix.c:1075:(snd_pcm_dmix_open) unable to open slave
aplay: main:830: audio open error: Device or resource busy

Solution: You need to enable the ALSA backend for PipeWire:

$ sudo touch /etc/pipewire/media-session.d/with-alsa
$ systemctl --user restart pipewire pipewire-pulse

Short cryptsetup/LUKS tutorial

This short tutorial will guide you in encrypting a drive with cryptsetup and LUKS scheme.

Before starting, if the device had previous data on it, it’s best to delete any filesystem signatures that may be on it. Assuming that the drive we operate is /dev/sda you can use the following command to remove the signatures:

$ sudo wipefs --all /dev/sda --no-act

Remove the --no-act flag to actually modify the disk.

The next step is to actually format the drive using LUKS. This is done using the cryptsetup utility.

$ sudo cryptsetup luksFormat --type=luks2 /dev/sda

WARNING!
========
This will overwrite data on /dev/sda irrevocably.

Are you sure? (Type 'yes' in capital letters): YES
Enter passphrase for /dev/sda: 
Verify passphrase: 

The command will prompt you to enter a passphrase for the encryption and should take a few seconds to complete.

The next step is to add an appropriate entry to crypttab which will simplify starting the dm-crypt mapping later. Add the following line to /etc/crypttab:

archive_crypt UUID=114d42e5-6aeb-4af0-8758-b4cc79dd1ba0 none luks,discard,noauto

where the UUID is obtained through lsblk /dev/sda -o UUID or a similar command. The archive_crypt is the name for the mapped device. It will appear as /dev/mapper/archive_crypt when the device is mapped. The none parameter specifies that no keyfile is used and the system should prompt for an encryption passphrase instead. The noauto, means not to attempt to load the device automatically upon boot. discard should be used if the underlying device is an SSD.

You can test everything works so far by opening and loading the LUKS device:

$ sudo cryptdisks_start archive_crypt

While the device is now encrypted, there is a possible leakage of metadata such as used blocks as an attacker can discern used vs unused blocks by examining the physical drive. This and other side-channel leaks can be mitigated by simply wiping the contents of the encrypted device.

$ openssl rand -hex 32 | openssl enc -chacha20 -in /dev/zero -pass stdin -nosalt | sudo dd if=/dev/stdin of=/dev/mapper/sda_crypt bs=4096 status=progress

We could also have used /dev/urandom but the above technique is much faster.

Now we can create the actual filesystem.

$ sudo mkfs.btrfs --label archive /dev/mapper/archive_crypt

At this point we’re actually pretty much done. You can add and entry to /etc/fstab to easily mount the filesystem and you’re done.

/dev/mapper/archive_crypt /home/guyru/archive btrfs noauto,user 0 0

Compiling lensfun-0.3.95 on Debian Buster

Lensfun provides lens distoration correction for Darktable and other raw processing applications. Version 0.3.95 provides ability to use the Adobe Camera Model, and hence use Adobe lens profiles (lcp files). However, lensfun 0.3.95 is not packaged for Debian Buster. Also Darktable won’t compile against the latest git version of Lensfun, so you must compile and install specifically version 0.3.95 to get ACM support.

We begin by downloading and extracting Lensfun 0.3.95. Lensfun 0.3.95 is not tagged in git, so we have to download the release directly from SourceForge. The release is not available from the GitHub repository.

$ wget https://sourceforge.net/projects/lensfun/files/0.3.95/lensfun-0.3.95.tar.gz
$ tar -xvf lensfun-0.3.95.tar.gz
$ cd lensfun-0.3.95/

Lensfun uses CMake for building and has also has CPack enabled. We can use it to build a deb package and install it. This allows easier integration and uninstallation in the future.

$ mkdir build
$ cd build
$ cmake -DCMAKE_BUILD_TYPE=release -DCPACK_BINARY_DEB=ON ../
$ make -j`nproc` && make package
$ sudo apt install ./liblensfun2_0.3.95.0_amd64.deb

Getting Radeon RX 550 to work under Debian Stretch

There are three things that need to be updated in Debian Stretch in order to get the Radeon RX 550 running properly (or at all): kernel, mesa and proprietary binary firmware (bummer, I know).

First thing, make sure you have stretch-backports in your apt-sources with all the relevant components.

$ deb http://ftp.debian.org/debian stretch-backports main contrib non-free

Now, the kernel that currently comes with stretch (4.9.0-8) is missing some important configurations: CONFIG_DRM_AMDGPU_SI, and CONFIG_DRM_AMDGPU_CIK. So you will need to install the latest one from the backports which does have the correct configuration.

$ sudo apt install -t stretch-backports linux-image-amd64

Next thing is getting the proper firmware

$ sudo apt install -t stretch-backports firmware-linux-nonfree

This will also update the firmware-amd-graphics which provides the binary blobs that are needed by the amdgpu driver to work properly. The old version does not support the new Polaris 12 architecture used by the RX 550, while the version from the backports (20180825) does support Polaris 12.

Now comes the part of upgrading mesa. There are a bunch binary packages that are derived from the mesa source package and we need to upgrade each one of them to version 18 (or later, but 18 is what is provided by the backports). The following two commands will upgrade any mesa related package already installed and then re-mark them as automatically installed (just to keep things tidy as they were).

sudo apt install -t stretch-backports $(grep-status -S mesa -a -FStatus "install ok installed" -s Package -n | sort -u)
sudo apt-mark auto $(grep-status -S mesa -a -FStatus "install ok installed" -s Package -n | sort -u)

(credit for the last two lines). Now you can restart your computer and the RX 550 should work. You can test it using

$ DRI_PRIME=1 glxinfo | grep OpenGL
OpenGL vendor string: X.Org
OpenGL renderer string: Radeon 500 Series (POLARIS12, DRM 3.26.0, 4.18.0-0.bpo.1-amd64, LLVM 6.0.0)
OpenGL core profile version string: 4.5 (Core Profile) Mesa 18.1.6

The DRI_PRIME=1 is necessary, else glxinfo would use the integrated card.

This is not necessary, but if lspcidoes not properly display the RX 550, you will need to update the PCI IDs that are used to translate IDs to actual human-readable names.

$ sudo update-pciids

Final word, if you are using TLP for power management, it may not play nice with the RX 550. With TLP enabled I get pretty horrible performance out of it (regardless of being on AC or battery).

Google Adsense for Wordpres – No Plugin Needed

Adding Google Adsense ads to your your WordPress blog was a tedious task. Either you needed to manually modify your theme, or you had to use a plugin, such as Google’s own Adsense plugin. Even then, placements were limited and handling both mobile and desktop themes was complicated at best. Recently, two things have changed: Google retired the Adsense plugin and introduced Auto Ads.

At first, the situation seemed like it turned for the worse. Without the official plugin, you had to resort into using a third-party plugin or manually placing ads in your theme. But Auto ads made things much simpler. Instead of having to manually place your ads, you can let Google do it for you. It works great on both desktop and mobile theme.

The easiest way to enable Auto ads is using a child theme. First, you need to get the Auto ads ad code. Next, in your child theme’s functions.php add the following lines, making sure to replace the javascript snippet with your own one.

// Add Google Adsense
function my_google_adsense_header() {
?>
<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<script>
     (adsbygoogle = window.adsbygoogle || []).push({
          google_ad_client: "ca-pub-4066984350135216",
          enable_page_level_ads: true
     });
</script>
<?php
}
add_action( 'wp_head', 'my_google_adsense_header');

Google Analytics for WordPress

To set up Google Analytics tracking for WordPress you don’t need any third-party plugin. It can be easily done using a child theme. A child theme, is a code that modifies the current theme in a way that won’t interfere with future upgrades. To enable Google Analytics, start by creating a child theme using the official documentation.

Now, you need to get you Google Analytics tracking code. Over the years the tracking code had a few different versions. You should make sure you are getting the latest tracking code, which currently looks like:

<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-380837-9"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', 'UA-380837-9');
</script>

To get the tracking code, follow the instructions on this page.

Now we add the tracking code to each page using the child theme. In your child theme’s directory, edit the functions.php file and add the following lines. Replace the tracking code with the one you acquired.

// Add Google Analytics tracking
function my_google_analytics_header() {
?>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-380837-9"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', 'UA-380837-9');
</script>
<?php
}
add_action( 'wp_head', 'my_google_analytics_header');

This adds the tracking code to the <head> of every page.

Creating Local Backups using `rdiff-backup`

rdiff-backup provides an easy way to maintain reverse-incremental backups of your data. Reverse incremental backups are different from normal incremental backups by synthetically updating the full backup and keeping reverse diffs of all the files changed. It is best illustrated by an example. Let’s consider backups taken on three consecutive days:
1. Full backup (1st day).
2. Full backup (2nd day), reverse-diff: 2nd -> 1st.
3. Full backup (3rd day), reverse diffs: 3rd -> 2nd, 2nd -> 1st.

Compare that with the regular incremental backup model which would be:
1. Full backup (1st day).
2. Diff: 2nd -> 1st, full backup (1st day).
3. Diffs: 3rd -> 2nd, 2nd -> 1st, full backup (1st day).

This especially makes purging old backups easier. Reverse incremental backups allows you to simply purge the reverse-diffs as they expire. This happens because newer backups never depend on older ones. In contrast, in the regular incremental model, each incremental backup depends on each prior backup in the chain, going back to the full backups. Thus, you can’t remove the full backup until all the incremental backups that depend on it expire as well. This means that most of the time you need to keep more than one full backups, which takes up precious disk space.

rdiff-backup has some disadvantages as well:
1. Backups are not encrypted, making it unsuitable as-is for remote backups.
2. Only the reverse-diffs are compressed.

The advantages of rdiff-bakcup make it suitable to create local Time Machine-like backups.

The following script, set via cron to run daily, can be used to take backups of your home directory:

#! /bin/sh

SOURCE="/home/user/"
TARGET="/home/user/backups/rdiff-home/"

## Backup
rdiff-backup --exclude-if-present .nobackup --exclude-globbing-filelist /home/user/backups/home-exclude --print-statistics $SOURCE $TARGET

## Remove old data
rdiff-backup --remove-older-than 1M --force --print-statistics $TARGET

where `/home/user/backups/home-exclude should look like:

+ /home/user/Desktop
+ /home/user/Documents
+ /home/user/Music
+ /home/user/Pictures
+ /home/user/Videos
+ /home/user/.vim
+ /home/user/.vimrc
+ /home/user/.ssh
+ /home/user/.gnupg

**

In order to select only certain files and directories to backup.

The --exclude-if-present .nobackup allows you to easily add a .nobackup file to directories you wish to ignore. The --force argument when purging the old backups allows it to remove more than one expired backup in a single run.

Listing backup chains:

$ rdiff-backup -l ~/backups/rdiff-home/

Restoring files from the most recent backup is simple. Because rdiff-backup keeps the latest backup as a normal mirror on the disk, you can simply copy the file you need out of the backup directory. To restore older files:

$ rdiff-backup --restore-as-of 10D ~/backups/rdiff-home/.vimrc restored_vimrc