{"id":690,"date":"2025-07-19T13:02:16","date_gmt":"2025-07-19T13:02:16","guid":{"rendered":"https:\/\/wqrld.net\/blog\/?p=690"},"modified":"2025-07-19T13:59:18","modified_gmt":"2025-07-19T13:59:18","slug":"immutable-backup-setup","status":"publish","type":"post","link":"https:\/\/wqrld.net\/blog\/immutable-backup-setup\/","title":{"rendered":"secure backup setup"},"content":{"rendered":"\n<p>We all know we should have backups, but you often see ransomware groups also targeting backup storage. Luckily, there are some nice ways to make your backups-over-SSH more secure using a neat authorized_keys trick.<br><br>In short: You can add some options to your authorized_keys file to force connections with that key to run a specific command.<\/p>\n\n\n\n<p>These tricks should also work on Hetzner storage boxes, and most other providers that allow you to upload an authorized_keys file.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Borg (non-immutable)<\/h2>\n\n\n\n<p>At Ferox, we use the following for onsite quick-access borg backups:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>command=\"\/usr\/bin\/borg serve --restrict-to-path \/storage\/mcsystembackups\",restrict ssh-ed25519 AAAAC...<\/code><\/pre>\n\n\n\n<p>Since borg works over ssh, it tries to run borg serve on the remote host. This command basically replaces that borg serve command with one that is limited to a specific folder.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Borg (Immutable)<\/h2>\n\n\n\n<p>Keep in mind that this key will now not allow pruning or deleting backups, so you will have to schedule that from a trusted location\/key.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>command=\"borg serve --append-only \",restrict ssh-ed25519 AAAAC3NzaC1lZ...J borgmatic_offsite<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">rsync (Immutable)<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>command=\"rrsync -wo -no-del -no-overwrite DIR\",restrict,from=\"fdba:000:1c1c:bff6::1\" ssh-rsa AAAAB3Nza... root@box-to-backup<\/code><\/pre>\n\n\n\n<p>This requires the remote host to have rrsync, which we are unfortunately missing on hetzner&#8217;s storage boxes.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Age + SCP (Encrypted)<\/h2>\n\n\n\n<p>Age is a nice tool for simple encrypted files. Useful for things like offsite backups: <a href=\"https:\/\/github.com\/FiloSottile\/age\">https:\/\/github.com\/FiloSottile\/age<\/a><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ apt install age \n$ age-keygen -o backupkey.txt\nPublic key: agepublickkeythingy \n$ age --decrypt -i \/opt\/backupkey.txt data.tar.gz.age > data.tar.gz<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>cat \/tmp\/billingdbbackups\/billingbackup-$(date +%d-%m-%Y).sql.gz | age -r agepublickkeythingy > \/tmp\/billingbackup.sql.gz.age\nscp -P23 \/tmp\/billingbackup.sql.gz.age u123-sub2@u123.your-storagebox.de:\/home\/agebackups\/billingbackup-$(date +%d-%m-%Y).sql.gz.age<\/code><\/pre>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>We all know we should have backups, but you often see ransomware groups also targeting backup storage. Luckily, there are some nice ways to make your backups-over-SSH more secure using a neat authorized_keys trick. In short: You can add some options to your authorized_keys file to force connections with that key to run a specific &hellip; <a href=\"https:\/\/wqrld.net\/blog\/immutable-backup-setup\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;secure backup setup&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-690","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/wqrld.net\/blog\/wp-json\/wp\/v2\/posts\/690","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wqrld.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/wqrld.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/wqrld.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/wqrld.net\/blog\/wp-json\/wp\/v2\/comments?post=690"}],"version-history":[{"count":7,"href":"https:\/\/wqrld.net\/blog\/wp-json\/wp\/v2\/posts\/690\/revisions"}],"predecessor-version":[{"id":697,"href":"https:\/\/wqrld.net\/blog\/wp-json\/wp\/v2\/posts\/690\/revisions\/697"}],"wp:attachment":[{"href":"https:\/\/wqrld.net\/blog\/wp-json\/wp\/v2\/media?parent=690"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/wqrld.net\/blog\/wp-json\/wp\/v2\/categories?post=690"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/wqrld.net\/blog\/wp-json\/wp\/v2\/tags?post=690"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}