Refactored Borg guide and added Debian server and Borg macOS client guides

This commit is contained in:
Sun Knudsen 2020-12-03 06:31:15 -05:00
parent 91365d1b57
commit 424cf6d207
No known key found for this signature in database
GPG key ID: 1FA767862BBD1305
16 changed files with 1031 additions and 729 deletions

View file

@ -0,0 +1,514 @@
<!--
Title: How to configure Borg client on macOS using command-line
Description: Learn how to configure Borg client on macOS using command-line.
Author: Sun Knudsen <https://github.com/sunknudsen>
Contributors: Sun Knudsen <https://github.com/sunknudsen>
Reviewers:
Publication date: 2020-11-27T18:07:56.515Z
Listed: true
-->
# How to configure Borg client on macOS using command-line
## Requirements
- Borg server ([self-hosted](../how-to-self-host-hardened-borg-server) or cloud-based such as [BorgBase](https://www.borgbase.com/) or [rsync.net](https://rsync.net/products/attic.html))
- Computer running macOS Mojave or Catalina
## Caveats
- When copy/pasting commands that start with `$`, strip out `$` as this character is not part of the command
- When copy/pasting commands that start with `cat << "EOF"`, select all lines at once (from `cat << "EOF"` to `EOF` inclusively) as they are part of the same (single) command
## Setup guide
> Heads-up: steps 1 to 4 are only required if using BorgBase or rsync.net (dont forget to enable 2FA).
### Step 1: create `borg` SSH key pair (if using BorgBase or rsync.net)
When asked for file in which to save key, enter `borg`.
When asked for passphrase, use output from `openssl rand -base64 24` (and store passphrase in password manager).
Use `borg.pub` public key when configuring Borg server.
```console
$ mkdir -p ~/.ssh
$ cd ~/.ssh
$ ssh-keygen -t rsa -C "borg"
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/sunknudsen/.ssh/id_rsa): borg
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in borg.
Your public key has been saved in borg.pub.
The key fingerprint is:
SHA256:b4YxePgBjP9hB/wPFz7MkzM5fDYEBtbtOBd7kxRTicY borg
The key's randomart image is:
+---[RSA 3072]----+
| oo+..o=|
| o . . ..Eoo.|
| . o o oooo.|
| . + o =o=+o.|
| + S + #o+..|
| = O + O . |
| + + . |
| o |
| |
+----[SHA256]-----+
$ cat borg.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQClMqEv1xTTWrz9cRGFsjtQ5ieK7sMs2eUMyROg1emhblUmGd6cMMfQDFDlwXUXk7ZPDHIkN3k9recff1oa3tvW+9D2oqGSyG0WOXqbZNHXZUSEhb9giOlVij0kOjfVbMR37zMZn+e6cVzq75Kn5B/ZSm9pfpWI5p4sHEn9S8TvoSgvCCu67bWc3UHHedd9dK5kJUPHNHvZUf+ebNo69iZuKE9HSP7eifGx5DszkU5cs6DPivAvRGgGer7Um2piQ+T7q+XcKo0JcaXVaObDZSGTZwiF8xAFDF1bfCl9jna26ZqqPKHdJJTEl8gaj9MQH6vlsAZ40xeFyCxiG0AhVpQ6SeeIN2qkf6k7EDyUQNcCmwY23THhFhEjfjuq6mbsuCK52tUx7bDMF8wed0lQ5k7OLuQuwyxDUinz3aBwboUQxxHfzImgKXzIrZ0hPge3fIgtFUBiUwFUv5xnTzBIStP5BFf5Ca5oxRq4rJDORnD0wMuMTWSyGZFVU5iEVml0Jhk= borg
```
### Step 2: create `borg-append-only` SSH key pair (if using BorgBase or rsync.net)
When asked for file in which to save key, enter `borg-append-only`.
When asked for passphrase, leave field empty for no passphrase (this public key will be used for append-only operations).
Use `borg-append-only.pub` public key when configuring Borg server.
```console
$ ssh-keygen -t rsa -C "borg-append-only"
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/sunknudsen/.ssh/id_rsa): borg-append-only
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in borg-append-only.
Your public key has been saved in borg-append-only.pub.
The key fingerprint is:
SHA256:xR8BvPMujEM955VubA/TWVlqt/Nt2INNX4UIw3wtssw borg-append-only
The key's randomart image is:
+---[RSA 3072]----+
| +.... |
| .B o.. |
| ooB.o ..|
| .E.....+|
| S. o. oo+|
| . o o.o+=|
| . o = +**+|
| o o o.*=B|
| . . o o=|
+----[SHA256]-----+
$ cat borg-append-only.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDsEfUNEZToWjefcGr8Dy/d+6ILuklWjC18E3ziaCZPNzKZAMfZTXm0CqKYgwRH5UXgYz//3gLPLNLtlHNeluVXSzLO1pxc+2Au19JOfzgcy86A3y4Gx8lFh80VyHhm33LjHsKsgacF2C0tKDBaJ/WqwDpX0m+E1WHCF0xZ7QdgGEoqj31yJ34WCeOXOro1yJfrV98iVWKuokCMHboaQoXTNu4+AMzGw/1MPUgmkT1nGnBpN5lP1v+kwAXAemC+A+Aw8gLf3pq84uAOhiTficH57PiyasJtwll5loDinkhnBtYhPHO9qN+M+n0by3rmIhsEIukdpwiI5Qm4LNTm6i53NiX1rfN2ln4SvqwVG7mmkqP9PbJXsgtD6mNjXOhncHvHeTbEb8IAHg28hGpq1rn8284+2jvviw9FMAzIgkeLRmAHz+XVAOmZDkn0128H4bYXAOeLISxTbgY1WAWzGnW+kCYbmQV3e8wAyOrp8mfZ1LgMvfc2/o0D9828Zy5UP4c= borg-append-only
```
### Step 3: configure SSH keys and create repo (if using BorgBase)
#### Configure SSH keys
Go to [SSH Keys](https://www.borgbase.com/account) and add `borg.pub` and `borg-append-only.pub` keys.
![Add SSH key](./borgbase-add-ssh-key.png?shadow=1)
![SSH keys](./borgbase-ssh-keys.png?shadow=1)
#### Create repo
Go to [Repositories](https://www.borgbase.com/account) and add repository.
![Add repository](./borgbase-add-repository.png?shadow=1)
![Repositories](./borgbase-repositories.png?shadow=1)
### Step 4: generate and upload `authorized_keys` file (if using rsync.net)
#### Set temporary environment variable
```shell
BORG_STORAGE_QUOTA="500G"
```
#### Generate `authorized_keys` file
Replace `18434` with rsync.net username.
```shell
cat << EOF > ~/Desktop/authorized_keys
command="borg1 serve --restrict-to-repository /data1/home/18434/backup --storage-quota $BORG_STORAGE_QUOTA",restrict $(cat ~/.ssh/borg.pub)
command="borg1 serve --append-only --restrict-to-repository /data1/home/18434/backup --storage-quota $BORG_STORAGE_QUOTA",restrict $(cat ~/.ssh/borg-append-only.pub)
EOF
```
#### Upload `authorized_keys` file
```shell
scp ~/Desktop/authorized_keys 18434@ch-s011.rsync.net:.ssh/authorized_keys
```
### Step 5: install [Homebrew](https://brew.sh/)
```shell
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
```
### Step 6: disable Homebrew analytics
```shell
brew analytics off
```
### Step 7: install [FUSE for macOS](https://osxfuse.github.io/), [Borg](https://www.borgbackup.org/) and [Borg Wrapper](https://github.com/sunknudsen/borg-wrapper)
> Heads-up: when installing Borg using `brew install borgbackup`, one can no longer run `brew mount` (see issue [#5522](https://github.com/borgbackup/borg/issues/5522)) so I created a [tap](https://github.com/borgbackup/homebrew-tap) that includes a patched version of [borgbackup](https://formulae.brew.sh/formula/borgbackup) called [borgbackup-llfuse](https://github.com/borgbackup/homebrew-tap/blob/master/Formula/borgbackup-llfuse.rb).
> Heads-up: if `brew install --cask osxfuse` fails, try `brew cask install osxfuse` (see [#9382](https://github.com/Homebrew/brew/issues/9382)).
```shell
brew install --cask osxfuse
brew install borgbackup/tap/borgbackup-llfuse
brew install --cask sunknudsen/tap/borg-wrapper
```
### Step 8: configure Borg
#### Generate Borg passphrase using `openssl` and add passphrase to “Keychain Access”
```shell
security add-generic-password -D secret -U -a $USER -s borg-passphrase -w $(openssl rand -base64 24)
```
#### Initialize Borg repo
Replace `borg@185.112.147.115:backup` with self-hosted or cloud-based repo.
```console
$ export BORG_PASSCOMMAND="security find-generic-password -a $USER -s borg-passphrase -w"
$ export BORG_RSH="ssh -i ~/.ssh/borg"
$ borg init --encryption=keyfile-blake2 "borg@185.112.147.115:backup"
Enter passphrase for key '/Users/sunknudsen/.ssh/borg':
By default repositories initialized with this version will produce security
errors if written to with an older version (up to and including Borg 1.0.8).
If you want to use these older versions, you can disable the check by running:
borg upgrade --disable-tam ssh://borg@185.112.147.115/./backup
See https://borgbackup.readthedocs.io/en/stable/changes.html#pre-1-0-9-manifest-spoofing-vulnerability for details about the security implications.
IMPORTANT: you will need both KEY AND PASSPHRASE to access this repo!
Use "borg key export" to export the key, optionally in printable format.
Write down the passphrase. Store both at safe place(s).
```
#### Backup `~/.config/borg` and `~/Library/Keychains` folders
> Heads-up: both key (stored in `~/.config/borg`) and passphrase (stored in `~/Library/Keychains`) are required to decrypt backup.
### Step 9: set temporary environment variables
Replace `borg@185.112.147.115:backup` with self-hosted or cloud-based repo and set backup name.
```shell
BORG_REPO="borg@185.112.147.115:backup"
BACKUP_NAME="$USER-macbook-pro"
```
### Step 10: create `/usr/local/bin/borg-backup.sh` script
```shell
cat << EOF > /usr/local/bin/borg-backup.sh
#! /bin/sh
set -e
repo="$BORG_REPO"
prefix="$BACKUP_NAME-"
export BORG_PASSCOMMAND="security find-generic-password -a $USER -s borg-passphrase -w"
export BORG_RSH="ssh -i ~/.ssh/borg-append-only"
borg create \\
--filter "AME" \\
--list \\
--stats \\
--verbose \\
"\$repo::\$prefix{now:%F-%H%M%S}" \\
"/Users/$USER/.ssh" \\
"/Users/$USER/Library/Keychains"
printf "%s\n" "Done"
EOF
chmod +x /usr/local/bin/borg-backup.sh
```
### Step 11: edit `/usr/local/bin/borg-backup.sh` script
```shell
vi /usr/local/bin/borg-backup.sh
```
### Step 12: create `/usr/local/bin/borg-list.sh` script
```shell
cat << EOF > /usr/local/bin/borg-list.sh
#! /bin/sh
set -e
prefix="$BACKUP_NAME-"
repo="$BORG_REPO"
export BORG_PASSCOMMAND="security find-generic-password -a $USER -s borg-passphrase -w"
export BORG_RSH="ssh -i ~/.ssh/borg-append-only"
borg list --prefix "\$prefix" "\$repo"
printf "%s\n" "Done"
EOF
chmod +x /usr/local/bin/borg-list.sh
```
### Step 13: create `/usr/local/bin/borg-check.sh` script
```shell
cat << EOF > /usr/local/bin/borg-check.sh
#! /bin/sh
set -e
prefix="$BACKUP_NAME-"
repo="$BORG_REPO"
export BORG_PASSCOMMAND="security find-generic-password -a $USER -s borg-passphrase -w"
export BORG_RSH="ssh -i ~/.ssh/borg-append-only"
borg check --prefix "\$prefix" "\$repo"
printf "%s\n" "Done"
EOF
chmod +x /usr/local/bin/borg-check.sh
```
### Step 14: create `/usr/local/bin/borg-restore.sh` script
```shell
cat << EOF > /usr/local/bin/borg-restore.sh
#! /bin/sh
set -e
function umount()
{
if [ -d "\$mount_point" ]; then
borg umount \$mount_point
fi
}
trap umount ERR INT
mount_point="\${TMPDIR}borg"
prefix="$BACKUP_NAME-"
repo="$BORG_REPO"
mkdir -p \$mount_point
export BORG_PASSCOMMAND="security find-generic-password -a $USER -s borg-passphrase -w"
export BORG_RSH="ssh -i ~/.ssh/borg-append-only"
borg mount --prefix "\$prefix" "\$repo" "\$mount_point"
open \$mount_point
printf "Restore data and press enter"
read -r answer
umount
printf "%s\n" "Done"
EOF
chmod +x /usr/local/bin/borg-restore.sh
```
### Step 15: create `/usr/local/bin/borg-prune.sh` script
```shell
cat << EOF > /usr/local/bin/borg-prune.sh
#! /bin/sh
set -e
prefix="$BACKUP_NAME-"
repo="$BORG_REPO"
export BORG_PASSCOMMAND="security find-generic-password -a $USER -s borg-passphrase -w"
export BORG_RSH="ssh -i ~/.ssh/borg"
borg prune --keep-hourly 24 --keep-daily 31 --keep-weekly 52 --keep-monthly -1 --list --prefix "\$prefix" --stats "\$repo"
printf "%s\n" "Done"
EOF
chmod +x /usr/local/bin/borg-prune.sh
```
### Step 16: create `/usr/local/var/log` folder
```shell
mkdir -p /usr/local/var/log
```
### Step 17: run “Borg Wrapper”
```shell
open /Applications/Borg\ Wrapper.app
```
> Heads-up: given “Borg Wrapper” is developed outside the [Apple Developer Program](https://developer.apple.com/programs/), macOS prevents opening the app without explicit user consent (granted by clicking “Open Anyway” in “System Preferences” / “Privacy & Security”).
![Allow app 1](./allow-app-1.png?shadow=1&width=420)
![Allow app 2](./allow-app-2.png?shadow=1&width=668)
![Allow app 3](./allow-app-3.png?shadow=1&width=475)
Backup completed
👍
### Step 18: schedule backup every hour using launchd
```shell
mkdir -p ~/Library/LaunchAgents
cat << EOF > ~/Library/LaunchAgents/local.borg-wrapper.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>Borg Wrapper.app</string>
<key>ProgramArguments</key>
<array>
<string>open</string>
<string>/Applications/Borg Wrapper.app</string>
</array>
<key>RunAtLoad</key>
<false/>
<key>StartCalendarInterval</key>
<dict>
<key>Minute</key>
<integer>0</integer>
</dict>
</dict>
</plist>
EOF
launchctl load ~/Library/LaunchAgents/local.borg-wrapper.plist
```
👍
---
## Usage guide
### Backup
```console
$ borg-backup.sh
Creating archive at "borg@185.112.147.115:backup::sunknudsen-macbook-pro-{now:%F-%H%M%S}"
A /Users/sunknudsen/Library/Keychains/4FD89B1C-70AF-58EC-8026-35E97A08F9FE/keychain-2.db-wal
Remote: Storage quota: 314.36 kB out of 10.00 GB used.
Remote: Storage quota: 318.04 kB out of 10.00 GB used.
------------------------------------------------------------------------------
Archive name: sunknudsen-macbook-pro-2020-12-02-081439
Archive fingerprint: 781c5ca9dac166264250bdbe2c87aa1f9fb5f817cafd66d1e720dfdaa443f625
Time (start): Wed, 2020-12-02 08:14:41
Time (end): Wed, 2020-12-02 08:14:42
Duration: 0.29 seconds
Number of files: 28
Utilization of max. archive size: 0%
------------------------------------------------------------------------------
Original size Compressed size Deduplicated size
This archive: 6.81 MB 312.77 kB 3.54 kB
All archives: 13.62 MB 625.53 kB 316.18 kB
Unique chunks Total chunks
Chunk index: 29 56
------------------------------------------------------------------------------
Done
```
Done
👍
### List
```console
$ borg-list.sh
sunknudsen-macbook-pro-2020-12-02-081338 Wed, 2020-12-02 08:13:41 [c01b3400ec076adb993a2c268cd48810da9acc122614cdc4c87e00075464c1ee]
sunknudsen-macbook-pro-2020-12-02-081439 Wed, 2020-12-02 08:14:41 [781c5ca9dac166264250bdbe2c87aa1f9fb5f817cafd66d1e720dfdaa443f625]
Done
```
Done
👍
### Check
```console
$ borg-check.sh
Done
```
Done
👍
### Restore
```console
$ borg-restore.sh
mount_osxfuse: the file system is not available (1)
umount: /var/folders/dl/mbmsd2m51nb8dvhmtz114j8w0000gn/T/borg: not currently mounted
```
> Heads-up: given “FUSE for macOS” is a third-party extension, macOS prevents using the extension without explicit user consent (granted by clicking “Allow” in “System Preferences” / “Privacy & Security”).
![Allow extension 1](./allow-extension-1.png?shadow=1&width=420)
![Allow extension 2](./allow-extension-2.png?shadow=1&width=668)
```console
$ borg-restore.sh
Restore data and press enter
Done
```
Done
👍
### Prune
```console
$ borg-prune.sh
Enter passphrase for key '/Users/sunknudsen/.ssh/borg':
Keeping archive: sunknudsen-macbook-pro-2020-12-02-081439 Wed, 2020-12-02 08:14:41 [781c5ca9dac166264250bdbe2c87aa1f9fb5f817cafd66d1e720dfdaa443f625]
Pruning archive: sunknudsen-macbook-pro-2020-12-02-081338 Wed, 2020-12-02 08:13:41 [c01b3400ec076adb993a2c268cd48810da9acc122614cdc4c87e00075464c1ee] (1/1)
------------------------------------------------------------------------------
Original size Compressed size Deduplicated size
Deleted data: -6.81 MB -312.76 kB -3.52 kB
All archives: 6.81 MB 312.77 kB 312.65 kB
Unique chunks Total chunks
Chunk index: 27 28
------------------------------------------------------------------------------
Done
```
Done
👍

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 451 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 KiB

View file

@ -0,0 +1,326 @@
<!--
Title: How to configure hardened Debian server
Description: Learn how to configure hardened Debian server.
Author: Sun Knudsen <https://github.com/sunknudsen>
Contributors: Sun Knudsen <https://github.com/sunknudsen>
Reviewers:
Publication date: 2020-11-27T10:00:26.806Z
Listed: true
-->
# How to configure hardened Debian server
## Requirements
- Virtual private server (VPS) or dedicated server running Debian 10 (buster)
- Linux or macOS computer
## Caveats
- When copy/pasting commands that start with `$`, strip out `$` as this character is not part of the command
- When copy/pasting commands that start with `cat << "EOF"`, select all lines at once (from `cat << "EOF"` to `EOF` inclusively) as they are part of the same (single) command
## Guide
### Step 1: create `server` SSH key pair (on computer)
When asked for file in which to save key, enter `server`.
When asked for passphrase, use output from `openssl rand -base64 24` (and store passphrase in password manager).
Use `server.pub` public key when setting up server.
```console
$ mkdir -p ~/.ssh
$ cd ~/.ssh
$ ssh-keygen -t rsa -C "server"
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/sunknudsen/.ssh/id_rsa): server
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in server.
Your public key has been saved in server.pub.
The key fingerprint is:
SHA256:De1pasRJ2n0ggfRSWRJqrcensqboAc2i+/+/FxAo3xI server
The key's randomart image is:
+---[RSA 3072]----+
| ..o=+. |
| . E=o+ |
| o+o*.o |
| o .oOoB o |
|o o o.S.B . |
|.o o =.. |
|. . . + . |
| . o .+ . |
|.o+.o+o.oo |
+----[SHA256]-----+
$ cat server.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDP58i1vuFEe3zoHT+hZRh0YaXQY+ADa8OgBIoTji+AqzZRAa3ve8yDLwtoQKYpAZ2OcHoWDJP2pB/4unLJfKu6ILqKjRLkrnWvMGqcFs2QSVFg4ernmjiSAf3l2qrM+jxwElPEUo0Ht1GByEnWw2yfq0RNg0fukVrczWnUzvJMyhzhG2sjncFHIe2L6SPLlqRW46uHQBhFuHHb2gERV6smH/1ZS8YJtjq1klgVshhZWlBtodbIHo70owAeIpkeped966fSfzcAVksr3lTLR5jQyqgcTlDLj9vJn8nhGX0S/ETUs9dUNAOz0HWDvAaRyw95g/KWrctHvvng4VzjoU4qJlkjnhutoyDhz/medMnm4rkD6g6hOCkNKhMrCKby45TlMWFCZLjDwB70DZwqJChfWXlo0Ov0lah0a+ZgZ7Quz4yvzlrJt7vZkqFfr5LBI8AOB3yfFbeOZR564Q0jaH7C6yeRRvYVZkNCCZAVK9K2v1X7Bl0x42WN/MCzsA6embk= server
```
### Step 2: log in to server as root
Replace `185.112.147.115` with IP of server.
```shell
ssh root@185.112.147.115 -i ~/.ssh/server
```
### Step 3: disable root Bash history
```shell
echo "HISTFILESIZE=0" >> ~/.bashrc
source ~/.bashrc
```
### Step 4: set root password
When asked for password, use output from `openssl rand -base64 24` (and store password in password manager).
```shell
passwd
```
### Step 5: create server-admin user
When asked for password, use output from `openssl rand -base64 24` (and store password in password manager).
All other fields are optional, press <kbd>enter</kbd> to skip them and then press <kbd>Y</kbd>.
```console
$ adduser server-admin
Adding user `server-admin' ...
Adding new group `server-admin' (1000) ...
Adding new user `server-admin' (1000) with group `server-admin' ...
Creating home directory `/home/server-admin' ...
Copying files from `/etc/skel' ...
New password:
Retype new password:
passwd: password updated successfully
Changing the user information for server-admin
Enter the new value, or press ENTER for the default
Full Name []:
Room Number []:
Work Phone []:
Home Phone []:
Other []:
Is the information correct? [Y/n] Y
```
### Step 6: copy root `authorized_keys` file to server-admin home folder
```shell
mkdir /home/server-admin/.ssh
cp /root/.ssh/authorized_keys /home/server-admin/.ssh/authorized_keys
chown -R server-admin:server-admin /home/server-admin/.ssh
```
### Step 7: log out
```shell
exit
```
### Step 8: log in as server-admin
Replace `185.112.147.115` with IP of server.
```shell
ssh server-admin@185.112.147.115 -i ~/.ssh/server
```
### Step 9: disable server-admin Bash history
```shell
sed -i -E 's/^HISTSIZE=/#HISTSIZE=/' ~/.bashrc
sed -i -E 's/^HISTFILESIZE=/#HISTFILESIZE=/' ~/.bashrc
echo "HISTFILESIZE=0" >> ~/.bashrc
source ~/.bashrc
```
### Step 10: switch to root
When asked, enter root password.
```shell
su -
```
### Step 11: disable root login and password authentication
```shell
sed -i -E 's/^(#)?PermitRootLogin (prohibit-password|yes)/PermitRootLogin no/' /etc/ssh/sshd_config
sed -i -E 's/^(#)?PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
systemctl restart ssh
```
### Step 12: update APT index and upgrade packages
#### Update APT index
```shell
apt update
```
#### Upgrade packages
```shell
apt upgrade -y
```
### Step 13: install and configure Vim
#### Install Vim
```shell
apt install -y vim
```
#### Configure Vim
```shell
cat << "EOF" > ~/.vimrc
set encoding=UTF-8
set termencoding=UTF-8
set nocompatible
set backspace=indent,eol,start
set autoindent
set tabstop=2
set shiftwidth=2
set expandtab
set smarttab
set ruler
set paste
syntax on
EOF
```
### Step 14: set timezone (the following is for Montreal time)
See [https://en.wikipedia.org/wiki/List_of_tz_database_time_zones](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) for available timezones.
```shell
timedatectl set-timezone America/Montreal
```
### Step 15: configure sysctl (if server is IPv4-only)
> Heads-up: only run the following if server is IPv4-only.
```shell
cp /etc/sysctl.conf /etc/sysctl.conf.backup
cat << "EOF" >> /etc/sysctl.conf
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1
EOF
sysctl -p
```
### Step 16: install iptables-persistent
When asked to save current IPv4 or IPv6 rules, answer `Yes`.
```shell
apt install -y iptables-persistent
```
### Step 17: configure iptables
```shell
iptables -N SSH_BRUTE_FORCE_MITIGATION
iptables -A SSH_BRUTE_FORCE_MITIGATION -m recent --name SSH --set
iptables -A SSH_BRUTE_FORCE_MITIGATION -m recent --name SSH --update --seconds 300 --hitcount 10 -m limit --limit 1/second --limit-burst 100 -j LOG --log-prefix "iptables[ssh-brute-force]: "
iptables -A SSH_BRUTE_FORCE_MITIGATION -m recent --name SSH --update --seconds 300 --hitcount 10 -j DROP
iptables -A SSH_BRUTE_FORCE_MITIGATION -j ACCEPT
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -p tcp --dport 22 --syn -m conntrack --ctstate NEW -j SSH_BRUTE_FORCE_MITIGATION
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT
iptables -A OUTPUT -p tcp --dport 53 -m state --state NEW -j ACCEPT
iptables -A OUTPUT -p udp --dport 53 -m state --state NEW -j ACCEPT
iptables -A OUTPUT -p tcp --dport 80 -m state --state NEW -j ACCEPT
iptables -A OUTPUT -p udp --dport 123 -m state --state NEW -j ACCEPT
iptables -A OUTPUT -p tcp --dport 443 -m state --state NEW -j ACCEPT
iptables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -P FORWARD DROP
iptables -P INPUT DROP
iptables -P OUTPUT DROP
```
If server is IPv4-only, run:
```shell
ip6tables -P FORWARD DROP
ip6tables -P INPUT DROP
ip6tables -P OUTPUT DROP
```
If server is dual stack (IPv4 + IPv6) run:
```shell
ip6tables -A INPUT -i lo -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type destination-unreachable -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type packet-too-big -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type time-exceeded -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type parameter-problem -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type router-advertisement -m hl --hl-eq 255 -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type neighbor-solicitation -m hl --hl-eq 255 -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type neighbor-advertisement -m hl --hl-eq 255 -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type redirect -m hl --hl-eq 255 -j ACCEPT
ip6tables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
ip6tables -A OUTPUT -o lo -j ACCEPT
ip6tables -A OUTPUT -p ipv6-icmp --icmpv6-type destination-unreachable -j ACCEPT
ip6tables -A OUTPUT -p ipv6-icmp --icmpv6-type packet-too-big -j ACCEPT
ip6tables -A OUTPUT -p ipv6-icmp --icmpv6-type time-exceeded -j ACCEPT
ip6tables -A OUTPUT -p ipv6-icmp --icmpv6-type parameter-problem -j ACCEPT
ip6tables -A OUTPUT -p ipv6-icmp --icmpv6-type router-solicitation -m hl --hl-eq 255 -j ACCEPT
ip6tables -A OUTPUT -p ipv6-icmp --icmpv6-type neighbour-solicitation -m hl --hl-eq 255 -j ACCEPT
ip6tables -A OUTPUT -p ipv6-icmp --icmpv6-type neighbour-advertisement -m hl --hl-eq 255 -j ACCEPT
ip6tables -A OUTPUT -p tcp --dport 53 -m state --state NEW -j ACCEPT
ip6tables -A OUTPUT -p udp --dport 53 -m state --state NEW -j ACCEPT
ip6tables -A OUTPUT -p tcp --dport 80 -m state --state NEW -j ACCEPT
ip6tables -A OUTPUT -p udp --dport 123 -m state --state NEW -j ACCEPT
ip6tables -A OUTPUT -p tcp --dport 443 -m state --state NEW -j ACCEPT
ip6tables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
ip6tables -P FORWARD DROP
ip6tables -P INPUT DROP
ip6tables -P OUTPUT DROP
```
### Step 18: log out and log in to confirm iptables didnt block SSH
#### Log out
```shell
exit
exit
```
#### Log in
Replace `185.112.147.115` with IP of server.
```shell
ssh server-admin@185.112.147.115 -i ~/.ssh/server
```
#### Switch to root
When asked, enter root password.
```shell
su -
```
### Step 19: make iptables rules persistent
```shell
iptables-save > /etc/iptables/rules.v4
ip6tables-save > /etc/iptables/rules.v6
```
👍

View file

@ -1,729 +0,0 @@
<!--
Title: How to self-host a hardened Borg backup server and configure macOS client
Description: Learn how to self-host a hardened Borg backup server and configure macOS client.
Author: Sun Knudsen <https://github.com/sunknudsen>
Contributors: Sun Knudsen <https://github.com/sunknudsen>
Reviewers:
Publication date: 2020-11-10T20:35:16.488Z
Listed: true
-->
# How to self-host a hardened Borg backup server and configure macOS client
## Requirements
- Virtual private server (VPS) or dedicated server running Debian 10 (buster)
- Computer running macOS Mojave or Catalina
## Caveats
- When copy/pasting commands that start with `$`, strip out `$` as this character is not part of the command
- When copy/pasting commands that start with `cat << "EOF"`, select all lines at once (from `cat << "EOF"` to `EOF` inclusively) as they are part of the same (single) command
## Setup guide
### Step 1: create `server` SSH key pair (on Mac)
When asked for file in which to save key, enter `server`.
When asked for passphrase, use output from `openssl rand -base64 24` (and store passphrase in password manager).
Use `server.pub` public key when setting up server.
```console
$ mkdir -p ~/.ssh
$ cd ~/.ssh
$ ssh-keygen -t rsa -C "server"
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/sunknudsen/.ssh/id_rsa): server
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in server.
Your public key has been saved in server.pub.
The key fingerprint is:
SHA256:rZFDBWi8f6BcRR2gqzIWmWtBiLdk89znOSpZkZGOQH8 server
The key's randomart image is:
+---[RSA 3072]----+
| .. . o.o+o.. |
| ..o * o. . |
|. *.o+E+o. |
| + *.==.o+ |
| . B..=S.. |
| +++.=. |
| *o. =. |
| ooo . . |
| .. |
+----[SHA256]-----+
$ cat server.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCowL4nNnu5Ca3ixlMxD6vVUexhx7M214ElheY/Y3K1mGavd5H6ynhkF71DAgI3YOB3B9KM/IzvV+8ioY+FUVlovBrKwqXzBwb5fnAGPjymjRMY93nYVExICHjI6aQX+9CG1WxRMPhZpDo51sKXORpWQDbMG+CfDa5nmjVjysoCdqwJfd22WEDFIXTiUdVXC5EvJBWiC0MUAphRPmNF/fXyaZPoiL1RmNX7h6JsMQJC/iWHwYgQZhzQCuoAOnvEXKUnu6s7FEUOSbqHCnOuHzxVkDHg1yy667hhyOuwkPdUW276T44GgwicSg/T2IWmwf5cBmDzaSr21kaM00zeg+stqkIwKqdpd0PhV8tjIdKCm3H9GsCRpE0erXLhJVsQTjmmFaodvFyroRHeyH9VBqzYrJXMhG/iXwK8uCeOwGFUosddYw3jJ3sLgsRI34oGKSq9HIRd7P5gdxUZ8cJiZUCpfS4vI4cZDkyR5D8Xvupe/X2pS5Llc8wtiy1K3nxIEQE= server
```
### Step 2: create `borg` SSH key pair (on Mac)
When asked for file in which to save key, enter `borg`.
When asked for passphrase, leave field empty for no passphrase.
```console
$ ssh-keygen -t rsa -C "borg"
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/sunknudsen/.ssh/id_rsa): borg
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in borg.
Your public key has been saved in borg.pub.
The key fingerprint is:
SHA256:ZB293/YueacLtg2a5anHe/PXruP8YwZXwU/caebReSk borg
The key's randomart image is:
+---[RSA 3072]----+
| .. ..*|
| . ..E X*|
| o . .=.=|
| o . .o|
| S . ..|
| ...o|
| .= ooo|
| *oB*+*|
| +o=+X&O|
+----[SHA256]-----+
$ cat borg.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDawvaD0JmNz3XBChQq9OZROa0psAwH0BpHwGZ/4cuDiTnU8gt3gYXDANkS++SKbUNMJCQW3QgVNFhpk2SmWA8lLvlpuD5J3kvHNFNKFv/hBc0XxsVJlpASONeCwilnS5otIqnDT0+KFMOevZUMCumEoBtjQ3IQbGkpWgf3NJ6ysXkt6kLRk7PMls4E733v/T4XUJmeBWT6B0rECqWE3aFzrjjZbfyJho0Pp8VzjT3m4vknNCvvwionjXRti5ObvEo3OZGWQbuhwW63JPS+aldNX9Xo0VC8t2UuSzzy7OeGI+JB76Pw1RYfXaMeflHry3O50kCIWIWHPNblw0sBPBsRs9BDg4R8urwpdVYjyirw9cZHDA8lkPxh0WS3IbA7Q1iRlVrfJkm9r4SqohxwQUeHIT7lpsyDHObUDF3KejRCWtyCqabPJVHqvGIds6rjQQo9lP5JNkeHg+qg8Cw61FihGLrlFStvgx1pMbBo2TvMEsRo65psVYUyi79taqbdlZM= borg
```
### Step 3: generate SSH authorized keys heredoc (on Mac)
#### Set temporary environment variable
`BORG_STORAGE_QUOTA` backup storage quota
```shell
BORG_STORAGE_QUOTA="10G"
```
#### Generate heredoc (the output of following command will be used at [step 21](#step-21-configure-borgs-ssh-authorized-keys))
```shell
cat << _EOF
cat << EOF > /home/borg/.ssh/authorized_keys
command="borg serve --append-only --restrict-to-repository /home/borg/backup --storage-quota $BORG_STORAGE_QUOTA",restrict $(cat ~/.ssh/borg.pub)"
command="borg serve --restrict-to-repository /home/borg/backup --storage-quota $BORG_STORAGE_QUOTA",restrict $(cat ~/.ssh/server.pub)"
EOF
_EOF
```
### Step 4: log in to server as root
Replace `185.112.144.30` with IP of server.
```shell
ssh root@185.112.144.30 -i ~/.ssh/server
```
### Step 5: create `server-admin` user
When asked for password, use output from `openssl rand -base64 24` (and store password in password manager).
All other fields are optional, press <kbd>enter</kbd> to skip them and then press <kbd>Y</kbd>.
```console
$ adduser server-admin
Adding user `server-admin' ...
Adding new group `server-admin' (1000) ...
Adding new user `server-admin' (1000) with group `server-admin' ...
Creating home directory `/home/server-admin' ...
Copying files from `/etc/skel' ...
New password:
Retype new password:
passwd: password updated successfully
Changing the user information for server-admin
Enter the new value, or press ENTER for the default
Full Name []:
Room Number []:
Work Phone []:
Home Phone []:
Other []:
Is the information correct? [Y/n] Y
```
### Step 6: create `borg` user
When asked for password, use output from `openssl rand -base64 24` (and store password in password manager).
All other fields are optional, press <kbd>enter</kbd> to skip them and then press <kbd>Y</kbd>.
```console
$ adduser borg
Adding user `borg' ...
Adding new group `borg' (1000) ...
Adding new user `borg' (1000) with group `borg' ...
Creating home directory `/home/borg' ...
Copying files from `/etc/skel' ...
New password:
Retype new password:
passwd: password updated successfully
Changing the user information for borg
Enter the new value, or press ENTER for the default
Full Name []:
Room Number []:
Work Phone []:
Home Phone []:
Other []:
Is the information correct? [Y/n] Y
```
### Step 7: copy roots `authorized_keys` file to server-admins home folder
```shell
mkdir /home/server-admin/.ssh
cp /root/.ssh/authorized_keys /home/server-admin/.ssh/authorized_keys
chown -R server-admin:server-admin /home/server-admin/.ssh
```
### Step 8: set root password
When asked for password, use output from `openssl rand -base64 24` (and store password in password manager).
```shell
passwd
```
### Step 9: log out
```shell
exit
```
### Step 10: log in as `server-admin`
Replace `185.112.144.30` with IP of server.
```shell
ssh server-admin@185.112.144.30 -i ~/.ssh/server
```
### Step 11: switch to root
When asked, enter root password.
```shell
su -
```
### Step 12: update SSH config to disable root login and password authentication and restart SSH
```shell
sed -i -E 's/(#)?PermitRootLogin (prohibit-password|yes)/PermitRootLogin no/' /etc/ssh/sshd_config
sed -i -E 's/(#)?PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
systemctl restart ssh
```
### Step 13: update APT index and upgrade packages
#### Update APT index
```shell
apt update
```
#### Upgrade packages
```shell
apt upgrade -y
```
### Step 14: install and configure Vim
#### Install Vim
```shell
apt install -y vim
```
#### Configure Vim
```shell
cat << "EOF" > ~/.vimrc
set encoding=UTF-8
set termencoding=UTF-8
set nocompatible
set backspace=indent,eol,start
set autoindent
set tabstop=2
set shiftwidth=2
set expandtab
set smarttab
set ruler
set paste
syntax on
EOF
```
### Step 15: set timezone (the following is for Montreal time)
See [https://en.wikipedia.org/wiki/List_of_tz_database_time_zones](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) for available timezones.
```shell
timedatectl set-timezone America/Montreal
```
### Step 16: install iptables-persistent
When asked to save current IPv4 or IPv6 rules, answer `Yes`.
```shell
apt install -y iptables-persistent
```
### Step 17: configure iptables
```shell
iptables -N SSH_BRUTE_FORCE_MITIGATION
iptables -A SSH_BRUTE_FORCE_MITIGATION -m recent --name SSH --set
iptables -A SSH_BRUTE_FORCE_MITIGATION -m recent --name SSH --update --seconds 300 --hitcount 10 -m limit --limit 1/second --limit-burst 100 -j LOG --log-prefix "iptables[ssh-brute-force]: "
iptables -A SSH_BRUTE_FORCE_MITIGATION -m recent --name SSH --update --seconds 300 --hitcount 10 -j DROP
iptables -A SSH_BRUTE_FORCE_MITIGATION -j ACCEPT
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -p tcp --dport 22 --syn -m conntrack --ctstate NEW -j SSH_BRUTE_FORCE_MITIGATION
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT
iptables -A OUTPUT -p tcp --dport 53 -m state --state NEW -j ACCEPT
iptables -A OUTPUT -p udp --dport 53 -m state --state NEW -j ACCEPT
iptables -A OUTPUT -p tcp --dport 80 -m state --state NEW -j ACCEPT
iptables -A OUTPUT -p udp --dport 123 -m state --state NEW -j ACCEPT
iptables -A OUTPUT -p tcp --dport 443 -m state --state NEW -j ACCEPT
iptables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -P FORWARD DROP
iptables -P INPUT DROP
iptables -P OUTPUT DROP
```
If the server is IPv4-only, run:
```shell
ip6tables -P FORWARD DROP
ip6tables -P INPUT DROP
ip6tables -P OUTPUT DROP
```
If the server is dual stack (IPv4 + IPv6) run:
```shell
ip6tables -A INPUT -i lo -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type destination-unreachable -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type packet-too-big -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type time-exceeded -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type parameter-problem -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type router-advertisement -m hl --hl-eq 255 -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type neighbor-solicitation -m hl --hl-eq 255 -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type neighbor-advertisement -m hl --hl-eq 255 -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type redirect -m hl --hl-eq 255 -j ACCEPT
ip6tables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
ip6tables -A OUTPUT -o lo -j ACCEPT
ip6tables -A OUTPUT -p ipv6-icmp --icmpv6-type destination-unreachable -j ACCEPT
ip6tables -A OUTPUT -p ipv6-icmp --icmpv6-type packet-too-big -j ACCEPT
ip6tables -A OUTPUT -p ipv6-icmp --icmpv6-type time-exceeded -j ACCEPT
ip6tables -A OUTPUT -p ipv6-icmp --icmpv6-type parameter-problem -j ACCEPT
ip6tables -A OUTPUT -p ipv6-icmp --icmpv6-type router-solicitation -m hl --hl-eq 255 -j ACCEPT
ip6tables -A OUTPUT -p ipv6-icmp --icmpv6-type neighbour-solicitation -m hl --hl-eq 255 -j ACCEPT
ip6tables -A OUTPUT -p ipv6-icmp --icmpv6-type neighbour-advertisement -m hl --hl-eq 255 -j ACCEPT
ip6tables -A OUTPUT -p tcp --dport 53 -m state --state NEW -j ACCEPT
ip6tables -A OUTPUT -p udp --dport 53 -m state --state NEW -j ACCEPT
ip6tables -A OUTPUT -p tcp --dport 80 -m state --state NEW -j ACCEPT
ip6tables -A OUTPUT -p udp --dport 123 -m state --state NEW -j ACCEPT
ip6tables -A OUTPUT -p tcp --dport 443 -m state --state NEW -j ACCEPT
ip6tables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
ip6tables -P FORWARD DROP
ip6tables -P INPUT DROP
ip6tables -P OUTPUT DROP
```
### Step 18: log out and log in to confirm iptables didnt block SSH
#### Log out
```shell
exit
exit
```
#### Log in
Replace `185.112.144.30` with IP of server.
```shell
ssh server-admin@185.112.144.30 -i ~/.ssh/server
```
#### Switch to root
When asked, enter root password.
```shell
su -
```
### Step 19: make iptables rules persistent
```shell
iptables-save > /etc/iptables/rules.v4
ip6tables-save > /etc/iptables/rules.v6
```
### Step 20: install [Borg](https://github.com/borgbackup/borg)
```shell
apt install -y borgbackup
```
### Step 21: configure borgs SSH authorized keys
#### Switch to borg
```shell
su - borg
```
#### Create `.ssh` folder
```shell
mkdir -p /home/borg/.ssh
```
#### Create `/home/borg/.ssh/authorized_keys` using heredoc generated at [step 3](#step-3-generate-ssh-authorized-keys-heredoc-on-mac)
```shell
cat << EOF > /home/borg/.ssh/authorized_keys
command="borg serve --append-only --restrict-to-repository /home/borg/backup --storage-quota 10G",restrict ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDawvaD0JmNz3XBChQq9OZROa0psAwH0BpHwGZ/4cuDiTnU8gt3gYXDANkS++SKbUNMJCQW3QgVNFhpk2SmWA8lLvlpuD5J3kvHNFNKFv/hBc0XxsVJlpASONeCwilnS5otIqnDT0+KFMOevZUMCumEoBtjQ3IQbGkpWgf3NJ6ysXkt6kLRk7PMls4E733v/T4XUJmeBWT6B0rECqWE3aFzrjjZbfyJho0Pp8VzjT3m4vknNCvvwionjXRti5ObvEo3OZGWQbuhwW63JPS+aldNX9Xo0VC8t2UuSzzy7OeGI+JB76Pw1RYfXaMeflHry3O50kCIWIWHPNblw0sBPBsRs9BDg4R8urwpdVYjyirw9cZHDA8lkPxh0WS3IbA7Q1iRlVrfJkm9r4SqohxwQUeHIT7lpsyDHObUDF3KejRCWtyCqabPJVHqvGIds6rjQQo9lP5JNkeHg+qg8Cw61FihGLrlFStvgx1pMbBo2TvMEsRo65psVYUyi79taqbdlZM= borg"
command="borg serve --restrict-to-repository /home/borg/backup --storage-quota 10G",restrict ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCowL4nNnu5Ca3ixlMxD6vVUexhx7M214ElheY/Y3K1mGavd5H6ynhkF71DAgI3YOB3B9KM/IzvV+8ioY+FUVlovBrKwqXzBwb5fnAGPjymjRMY93nYVExICHjI6aQX+9CG1WxRMPhZpDo51sKXORpWQDbMG+CfDa5nmjVjysoCdqwJfd22WEDFIXTiUdVXC5EvJBWiC0MUAphRPmNF/fXyaZPoiL1RmNX7h6JsMQJC/iWHwYgQZhzQCuoAOnvEXKUnu6s7FEUOSbqHCnOuHzxVkDHg1yy667hhyOuwkPdUW276T44GgwicSg/T2IWmwf5cBmDzaSr21kaM00zeg+stqkIwKqdpd0PhV8tjIdKCm3H9GsCRpE0erXLhJVsQTjmmFaodvFyroRHeyH9VBqzYrJXMhG/iXwK8uCeOwGFUosddYw3jJ3sLgsRI34oGKSq9HIRd7P5gdxUZ8cJiZUCpfS4vI4cZDkyR5D8Xvupe/X2pS5Llc8wtiy1K3nxIEQE= server"
EOF
```
> Heads-up: the following steps are done on Mac.
### Step 22: download and install [FUSE for macOS](https://osxfuse.github.io/)
Go to [https://osxfuse.github.io/](https://osxfuse.github.io/), download and install latest release.
### Step 23: install [Homebrew](https://brew.sh/)
```shell
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
```
### Step 24: disable Homebrew analytics
```shell
brew analytics off
```
### Step 25: install [Borg](https://github.com/borgbackup/borg)
```shell
brew install borgbackup
```
### Step 26: configure Borg
#### Generate Borg passphrase using `openssl` and add passphrase to “Keychain Access”
```shell
security add-generic-password -D secret -U -a $USER -s borg-passphrase -w $(openssl rand -base64 24)
```
#### Initialize Borg repo
Replace `185.112.144.30` with IP of server.
```console
$ export BORG_PASSCOMMAND="security find-generic-password -a $USER -s borg-passphrase -w"
$ export BORG_RSH="ssh -i ~/.ssh/borg"
$ borg init --encryption=keyfile-blake2 "borg@185.112.144.30:backup"
By default repositories initialized with this version will produce security
errors if written to with an older version (up to and including Borg 1.0.8).
If you want to use these older versions, you can disable the check by running:
borg upgrade --disable-tam ssh://borg@185.112.144.30/./backup
See https://borgbackup.readthedocs.io/en/stable/changes.html#pre-1-0-9-manifest-spoofing-vulnerability for details about the security implications.
IMPORTANT: you will need both KEY AND PASSPHRASE to access this repo!
Use "borg key export" to export the key, optionally in printable format.
Write down the passphrase. Store both at safe place(s).
```
#### Backup `~/.config/borg` and `~/Library/Keychains` folders
> Heads-up: both key (stored in `~/.config/borg`) and passphrase (stored in `~/Library/Keychains`) are required to decrypt backup.
### Step 27: set temporary environment variables
Replace `185.112.144.30` with IP of server and set backup name.
```shell
SERVER_IP="185.112.144.30"
BACKUP_NAME="$USER-macbook-pro"
```
### Step 28: create `/usr/local/bin/borg-backup.sh` script
```shell
cat << EOF > /usr/local/bin/borg-backup.sh
#! /bin/sh
set -e
repo="borg@$SERVER_IP:backup"
prefix="$BACKUP_NAME-"
export BORG_PASSCOMMAND="security find-generic-password -a $USER -s borg-passphrase -w"
export BORG_RSH="ssh -i ~/.ssh/borg"
borg create \\
--filter "AME" \\
--list \\
--stats \\
--verbose \\
"\$repo::\$prefix{now:%F-%H%M%S}" \\
"/Users/$USER/.gnupg" \\
"/Users/$USER/.ssh" \\
"/Users/$USER/Library/Keychains"
printf "%s\n" "Done"
EOF
chmod +x /usr/local/bin/borg-backup.sh
```
### Step 29: edit `/usr/local/bin/borg-backup.sh` script
```shell
vi /usr/local/bin/borg-backup.sh
```
### Step 30: create `/usr/local/bin/borg-list.sh` script
```shell
cat << EOF > /usr/local/bin/borg-list.sh
#! /bin/sh
set -e
prefix="$BACKUP_NAME-"
repo="borg@$SERVER_IP:backup"
export BORG_PASSCOMMAND="security find-generic-password -a $USER -s borg-passphrase -w"
export BORG_RSH="ssh -i ~/.ssh/borg"
borg list --prefix "\$prefix" "\$repo"
printf "%s\n" "Done"
EOF
chmod +x /usr/local/bin/borg-list.sh
```
### Step 31: create `/usr/local/bin/borg-prune.sh` script
```shell
cat << EOF > /usr/local/bin/borg-prune.sh
#! /bin/sh
set -e
prefix="$BACKUP_NAME-"
repo="borg@$SERVER_IP:backup"
export BORG_PASSCOMMAND="security find-generic-password -a $USER -s borg-passphrase -w"
export BORG_RSH="ssh -i ~/.ssh/server"
borg prune --keep-daily 7 --keep-weekly 4 --keep-monthly 12 --list --prefix "\$prefix" --stats "\$repo"
printf "%s\n" "Done"
EOF
chmod +x /usr/local/bin/borg-prune.sh
```
### Step 32: create `/usr/local/bin/borg-restore.sh` script
```shell
cat << EOF > /usr/local/bin/borg-restore.sh
#! /bin/sh
set -e
function umount()
{
if [ -d "\$mount_point" ]; then
borg umount \$mount_point
fi
}
trap umount ERR INT
mount_point="\${TMPDIR}borg"
prefix="$BACKUP_NAME-"
repo="borg@$SERVER_IP:backup"
mkdir -p \$mount_point
export BORG_PASSCOMMAND="security find-generic-password -a $USER -s borg-passphrase -w"
export BORG_RSH="ssh -i ~/.ssh/borg"
borg mount --prefix "\$prefix" "\$repo" "\$mount_point"
open \$mount_point
printf "Restore data and press enter"
read -r answer
umount
printf "%s\n" "Done"
EOF
chmod +x /usr/local/bin/borg-restore.sh
```
### Step 33: install [Borg Backup](https://github.com/sunknudsen/borg-backup)
#### Go to [https://github.com/sunknudsen/borg-backup/releases](https://github.com/sunknudsen/borg-backup/releases) and download latest `.dmg` release ([PGP public key](https://sunknudsen.com/sunknudsen.asc))
#### Double-click `.dmg` release and drag and drop “Borg Backup” to the “Applications” folder
### Step 34: create `/usr/local/var/log` folder
```shell
mkdir -p /usr/local/var/log
```
### Step 35: run “Borg Backup”
```shell
open /Applications/Borg\ Backup.app
```
> Heads-up: given “Borg Backup” is developed outside the [Apple Developer Program](https://developer.apple.com/programs/), macOS prevents opening the app without explicit user consent (clicking “Open Anyway” in “System Preferences” / “Privacy & Security”).
![Allow app step 1](./allow-app-step-1.png?shadow=1&width=420)
![Allow app step 2](./allow-app-step-2.png?shadow=1&width=668)
![Allow app step 3](./allow-app-step-3.png?shadow=1&width=475)
### Step 36: schedule backup every hour using launchd
```shell
mkdir -p ~/Library/LaunchAgents
cat << EOF > ~/Library/LaunchAgents/local.borg-backup.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>Borg Backup.app</string>
<key>ProgramArguments</key>
<array>
<string>open</string>
<string>/Applications/Borg Backup.app</string>
</array>
<key>RunAtLoad</key>
<false/>
<key>StartCalendarInterval</key>
<dict>
<key>Minute</key>
<integer>0</integer>
</dict>
</dict>
</plist>
EOF
launchctl load ~/Library/LaunchAgents/local.borg-backup.plist
```
## Usage guide
### Backup
```console
$ borg-backup.sh
Creating archive at "borg@185.112.144.30:backup::sunknudsen-macbook-pro-{now:%F-%H%M%S}"
A /Users/sunknudsen/Library/Keychains/4FD89B1C-70AF-58EC-8026-35E97A08F9FE/keychain-2.db-wal
Remote: Storage quota: 667.73 kB out of 10.00 GB used.
Remote: Storage quota: 671.72 kB out of 10.00 GB used.
------------------------------------------------------------------------------
Archive name: sunknudsen-macbook-pro-2020-11-12-095711
Archive fingerprint: 943420e2bfd0154a953c7dc84a6c42f1ab49888e17453df0c63518b65512d718
Time (start): Thu, 2020-11-12 09:57:14
Time (end): Thu, 2020-11-12 09:57:15
Duration: 0.39 seconds
Number of files: 29
Utilization of max. archive size: 0%
------------------------------------------------------------------------------
Original size Compressed size Deduplicated size
This archive: 8.43 MB 666.16 kB 3.84 kB
All archives: 16.87 MB 1.33 MB 669.77 kB
Unique chunks Total chunks
Chunk index: 31 62
------------------------------------------------------------------------------
Done
```
Done
👍
### List
```console
$ borg-list.sh
sunknudsen-macbook-pro-2020-11-12-095625 Thu, 2020-11-12 09:56:28 [a8f8ef592501cc240da7f000b23626a4371719565d8c9e5ce55855b1fe465742]
sunknudsen-macbook-pro-2020-11-12-095711 Thu, 2020-11-12 09:57:14 [943420e2bfd0154a953c7dc84a6c42f1ab49888e17453df0c63518b65512d718]
Done
```
Done
👍
### Prune
```console
$ borg-prune.sh
Enter passphrase for key '/Users/sunknudsen/.ssh/server':
Keeping archive: sunknudsen-macbook-pro-2020-11-12-095711 Thu, 2020-11-12 09:57:14 [943420e2bfd0154a953c7dc84a6c42f1ab49888e17453df0c63518b65512d718]
Pruning archive: sunknudsen-macbook-pro-2020-11-12-095625 Thu, 2020-11-12 09:56:28 [a8f8ef592501cc240da7f000b23626a4371719565d8c9e5ce55855b1fe465742] (1/1)
------------------------------------------------------------------------------
Original size Compressed size Deduplicated size
Deleted data: -8.43 MB -666.16 kB -3.84 kB
All archives: 8.43 MB 666.16 kB 665.93 kB
Unique chunks Total chunks
Chunk index: 29 31
------------------------------------------------------------------------------
Done
```
Done
👍
### Restore
```console
$ borg-restore.sh
Restore data and press enter
Done
```
Done
👍

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

View file

@ -0,0 +1,191 @@
<!--
Title: How to self-host hardened Borg server
Description: Learn how to self-host hardened Borg server.
Author: Sun Knudsen <https://github.com/sunknudsen>
Contributors: Sun Knudsen <https://github.com/sunknudsen>
Reviewers:
Publication date: 2020-11-27T17:49:18.440Z
Listed: true
-->
# How to self-host hardened Borg server
## Requirements
- [Hardened Debian server](../how-to-configure-hardened-debian-server) 📦
- Linux or macOS computer
## Caveats
- When copy/pasting commands that start with `$`, strip out `$` as this character is not part of the command
- When copy/pasting commands that start with `cat << "EOF"`, select all lines at once (from `cat << "EOF"` to `EOF` inclusively) as they are part of the same (single) command
## Setup guide
### Step 1: create `borg` SSH key pair (on computer)
When asked for file in which to save key, enter `borg`.
When asked for passphrase, use output from `openssl rand -base64 24` (and store passphrase in password manager).
```console
$ mkdir -p ~/.ssh
$ cd ~/.ssh
$ ssh-keygen -t rsa -C "borg"
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/sunknudsen/.ssh/id_rsa): borg
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in borg.
Your public key has been saved in borg.pub.
The key fingerprint is:
SHA256:b4YxePgBjP9hB/wPFz7MkzM5fDYEBtbtOBd7kxRTicY borg
The key's randomart image is:
+---[RSA 3072]----+
| oo+..o=|
| o . . ..Eoo.|
| . o o oooo.|
| . + o =o=+o.|
| + S + #o+..|
| = O + O . |
| + + . |
| o |
| |
+----[SHA256]-----+
$ cat borg.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQClMqEv1xTTWrz9cRGFsjtQ5ieK7sMs2eUMyROg1emhblUmGd6cMMfQDFDlwXUXk7ZPDHIkN3k9recff1oa3tvW+9D2oqGSyG0WOXqbZNHXZUSEhb9giOlVij0kOjfVbMR37zMZn+e6cVzq75Kn5B/ZSm9pfpWI5p4sHEn9S8TvoSgvCCu67bWc3UHHedd9dK5kJUPHNHvZUf+ebNo69iZuKE9HSP7eifGx5DszkU5cs6DPivAvRGgGer7Um2piQ+T7q+XcKo0JcaXVaObDZSGTZwiF8xAFDF1bfCl9jna26ZqqPKHdJJTEl8gaj9MQH6vlsAZ40xeFyCxiG0AhVpQ6SeeIN2qkf6k7EDyUQNcCmwY23THhFhEjfjuq6mbsuCK52tUx7bDMF8wed0lQ5k7OLuQuwyxDUinz3aBwboUQxxHfzImgKXzIrZ0hPge3fIgtFUBiUwFUv5xnTzBIStP5BFf5Ca5oxRq4rJDORnD0wMuMTWSyGZFVU5iEVml0Jhk= borg
```
### Step 2: create `borg-append-only` SSH key pair (on computer)
When asked for file in which to save key, enter `borg-append-only`.
When asked for passphrase, leave field empty for no passphrase.
```console
$ ssh-keygen -t rsa -C "borg-append-only"
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/sunknudsen/.ssh/id_rsa): borg-append-only
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in borg-append-only.
Your public key has been saved in borg-append-only.pub.
The key fingerprint is:
SHA256:xR8BvPMujEM955VubA/TWVlqt/Nt2INNX4UIw3wtssw borg-append-only
The key's randomart image is:
+---[RSA 3072]----+
| +.... |
| .B o.. |
| ooB.o ..|
| .E.....+|
| S. o. oo+|
| . o o.o+=|
| . o = +**+|
| o o o.*=B|
| . . o o=|
+----[SHA256]-----+
$ cat borg-append-only.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDsEfUNEZToWjefcGr8Dy/d+6ILuklWjC18E3ziaCZPNzKZAMfZTXm0CqKYgwRH5UXgYz//3gLPLNLtlHNeluVXSzLO1pxc+2Au19JOfzgcy86A3y4Gx8lFh80VyHhm33LjHsKsgacF2C0tKDBaJ/WqwDpX0m+E1WHCF0xZ7QdgGEoqj31yJ34WCeOXOro1yJfrV98iVWKuokCMHboaQoXTNu4+AMzGw/1MPUgmkT1nGnBpN5lP1v+kwAXAemC+A+Aw8gLf3pq84uAOhiTficH57PiyasJtwll5loDinkhnBtYhPHO9qN+M+n0by3rmIhsEIukdpwiI5Qm4LNTm6i53NiX1rfN2ln4SvqwVG7mmkqP9PbJXsgtD6mNjXOhncHvHeTbEb8IAHg28hGpq1rn8284+2jvviw9FMAzIgkeLRmAHz+XVAOmZDkn0128H4bYXAOeLISxTbgY1WAWzGnW+kCYbmQV3e8wAyOrp8mfZ1LgMvfc2/o0D9828Zy5UP4c= borg-append-only
```
### Step 3: generate SSH authorized keys heredoc (on computer)
#### Set temporary environment variable
```shell
BORG_STORAGE_QUOTA="10G"
```
#### Generate heredoc (the output of following command will be used at [step 8](#create-homeborgsshauthorized_keys-using-heredoc-generated-at-step-2))
```shell
cat << EOF
cat << _EOF > /home/borg/.ssh/authorized_keys
command="borg serve --restrict-to-repository /home/borg/backup --storage-quota $BORG_STORAGE_QUOTA",restrict $(cat ~/.ssh/borg.pub)
command="borg serve --append-only --restrict-to-repository /home/borg/backup --storage-quota $BORG_STORAGE_QUOTA",restrict $(cat ~/.ssh/borg-append-only.pub)
_EOF
EOF
```
### Step 4: log in to server
Replace `185.112.147.115` with IP of server.
```shell
ssh server-admin@185.112.147.115 -i ~/.ssh/server
```
### Step 5: switch to root
When asked, enter root password.
```shell
su -
```
### Step 6: create `borg` user
When asked for password, use output from `openssl rand -base64 24` (and store password in password manager).
All other fields are optional, press <kbd>enter</kbd> to skip them and then press <kbd>Y</kbd>.
```console
$ adduser borg
Adding user `borg' ...
Adding new group `borg' (1000) ...
Adding new user `borg' (1000) with group `borg' ...
Creating home directory `/home/borg' ...
Copying files from `/etc/skel' ...
New password:
Retype new password:
passwd: password updated successfully
Changing the user information for borg
Enter the new value, or press ENTER for the default
Full Name []:
Room Number []:
Work Phone []:
Home Phone []:
Other []:
Is the information correct? [Y/n] Y
```
### Step 7: update APT index
```shell
apt update
```
### Step 8: install [Borg](https://github.com/borgbackup/borg)
```shell
apt install -y borgbackup
```
### Step 9: configure borg SSH authorized keys
#### Create `.ssh` folder
```shell
mkdir -p /home/borg/.ssh
```
#### Create `/home/borg/.ssh/authorized_keys` using heredoc generated at [step 2](#generate-heredoc-the-output-of-following-command-will-be-used-at-step-8)
```shell
cat << _EOF > /home/borg/.ssh/authorized_keys
command="borg serve --restrict-to-repository /home/borg/backup --storage-quota 10G",restrict ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQClMqEv1xTTWrz9cRGFsjtQ5ieK7sMs2eUMyROg1emhblUmGd6cMMfQDFDlwXUXk7ZPDHIkN3k9recff1oa3tvW+9D2oqGSyG0WOXqbZNHXZUSEhb9giOlVij0kOjfVbMR37zMZn+e6cVzq75Kn5B/ZSm9pfpWI5p4sHEn9S8TvoSgvCCu67bWc3UHHedd9dK5kJUPHNHvZUf+ebNo69iZuKE9HSP7eifGx5DszkU5cs6DPivAvRGgGer7Um2piQ+T7q+XcKo0JcaXVaObDZSGTZwiF8xAFDF1bfCl9jna26ZqqPKHdJJTEl8gaj9MQH6vlsAZ40xeFyCxiG0AhVpQ6SeeIN2qkf6k7EDyUQNcCmwY23THhFhEjfjuq6mbsuCK52tUx7bDMF8wed0lQ5k7OLuQuwyxDUinz3aBwboUQxxHfzImgKXzIrZ0hPge3fIgtFUBiUwFUv5xnTzBIStP5BFf5Ca5oxRq4rJDORnD0wMuMTWSyGZFVU5iEVml0Jhk= borg
command="borg serve --append-only --restrict-to-repository /home/borg/backup --storage-quota 10G",restrict ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDsEfUNEZToWjefcGr8Dy/d+6ILuklWjC18E3ziaCZPNzKZAMfZTXm0CqKYgwRH5UXgYz//3gLPLNLtlHNeluVXSzLO1pxc+2Au19JOfzgcy86A3y4Gx8lFh80VyHhm33LjHsKsgacF2C0tKDBaJ/WqwDpX0m+E1WHCF0xZ7QdgGEoqj31yJ34WCeOXOro1yJfrV98iVWKuokCMHboaQoXTNu4+AMzGw/1MPUgmkT1nGnBpN5lP1v+kwAXAemC+A+Aw8gLf3pq84uAOhiTficH57PiyasJtwll5loDinkhnBtYhPHO9qN+M+n0by3rmIhsEIukdpwiI5Qm4LNTm6i53NiX1rfN2ln4SvqwVG7mmkqP9PbJXsgtD6mNjXOhncHvHeTbEb8IAHg28hGpq1rn8284+2jvviw9FMAzIgkeLRmAHz+XVAOmZDkn0128H4bYXAOeLISxTbgY1WAWzGnW+kCYbmQV3e8wAyOrp8mfZ1LgMvfc2/o0D9828Zy5UP4c= borg-append-only
_EOF
```
#### Change ownership of `/home/borg/.ssh`
```
chown -R borg:borg /home/borg/.ssh
```
👍