Skip to content

Commit 126f804

Browse files
committed
added support for age encryption for automatic encryption against ssh or age keys after upload
1 parent 45063ca commit 126f804

File tree

7 files changed

+79
-6
lines changed

7 files changed

+79
-6
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<div align="center">
99

1010

11-
![](https://img.shields.io/badge/php-7.4%2B-brightgreen.svg)
11+
![](https://img.shields.io/badge/php-8.3%2B-brightgreen.svg)
1212
[![Apache License](https://img.shields.io/badge/license-Apache-brightgreen.svg?style=flat)](https://github.com/hascheksolutions/backupdrop/blob/master/LICENSE)
1313
![HitCount](http://hits.dwyl.com/hascheksolutions/backupdrop.svg)
1414
[![](https://img.shields.io/github/stars/hascheksolutions/backupdrop.svg?label=Stars&style=social)](https://github.com/hascheksolutions/backupdrop)

config/example.config.inc.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@
33
// copy this file to config.inc.php
44
// and edit to your needs
55

6+
7+
// AGE encryption settings
8+
// More info on age encryption: https://github.com/FiloSottile/age
9+
define('ENCRYPTION_AGE_SSH_PUBKEY',''); // Enter your SSH public key here to automatically encrypt all uploads
10+
define('ENCRYPTION_AGE_PUBKEY',''); // Enter an "age public key" created with `age-keygen -o key.txt` here to automatically encrypt all uploads with this key
11+
612
// global settings for retention and version control
713
// 0 means unlimited
814
define('KEEP_N_BACKUPS',0); // How many uploads will be saved. Oldest one will be deleted if this number is surpassed

docker/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
FROM alpine:3.20
22

33
RUN apk add --no-cache pwgen sudo socat wget curl git nginx \
4-
php83-ctype php83-ftp php83-simplexml php83 php83-phar php83-curl php83-openssl php83-mbstring php83-json php83-dom php83-fpm
4+
php83-ctype php83-ftp php83-simplexml php83 php83-phar php83-curl php83-openssl php83-mbstring php83-json php83-dom php83-fpm age
55

66
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/bin --filename=composer
77
RUN mkdir -p /var/www/backupdrop

docker/rootfs/start.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ nginx
2020
_buildConfig() {
2121
echo "<?php"
2222

23+
echo "define('ENCRYPTION_AGE_SSH_PUBKEY','${ENCRYPTION_AGE_SSH_PUBKEY:-}');"
24+
echo "define('ENCRYPTION_AGE_PUBKEY','${ENCRYPTION_AGE_PUBKEY:-}');"
25+
2326
echo "define('S3_BUCKET','${S3_BUCKET:-}');"
2427
echo "define('S3_ACCESS_KEY','${S3_ACCESS_KEY:-}');"
2528
echo "define('S3_SECRET_KEY','${S3_SECRET_KEY:-}');"

rtfm/encryption.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,27 @@
22

33
You really should encrypt before uploading it to BackupDrop but if that's not an option, we've got you covered.
44

5-
If you are encrypting on your machine I'd reccomend [Age](https://github.com/FiloSottile/age). It's awesome and easy to use and you can encrypt files by SSH public keys (so the encrypting machine never needs to have the private key needed to decrypt).
5+
If you are encrypting on your machine I'd reccomend [Age](https://github.com/FiloSottile/age). It's awesome and easy to use and you can encrypt files by SSH public keys (so the encrypting machine never needs to have the private key needed to decrypt). But BackupDrop comes with age support so all uploads can be encrypted using an SSH public key or an age public key (or both).
6+
7+
## Method 1: BackupDrop builtin age support
8+
To use age in BackupDrop you only have to set one of two (or both) configuration options in the config file:
9+
10+
- ENCRYPTION_AGE_SSH_PUBKEY
11+
- ENCRYPTION_AGE_PUBKEY
12+
13+
If you configure both entries then all uploads will be encrypted against both, your SSH public key and your age public key which means you can decrypt the backups with both of your (private) keys.
14+
15+
### SSH key based encryption
16+
You can put your SSH keys public key in the `ENCRYPTION_AGE_SSH_PUBKEY` option and age will automatically encrypt all uploads against your key.
17+
18+
For example if you upload `secrets.txt`, it will be stored as `secrets.txt.age` in the data directory. To decrypt you can run `age -d -i ~/.ssh/id_rsa secrets.txt.age > secrets.txt`
19+
20+
Read more about age and SSH key based encryption [here](https://github.com/FiloSottile/age?tab=readme-ov-file#ssh-keys)
21+
22+
### age public key encryption
23+
You can generate an age public and private key by running `age-keygen -o key.txt` which will generate a key file and print out the public key. You can put this public key in the config option `ENCRYPTION_AGE_PUBKEY` and all uploads will automatically be encrypted against your key.
24+
25+
For example if you upload `secrets.txt`, it will be stored as `secrets.txt.age` in the data directory. To decrypt you can run `age --decrypt -i key.txt secrets.txt.age > secrets.txt`
626

727
## Method 1: Encrypt using Public Key
828
This method should only be used on **smaller files**. Because of the nature of the algorithm we can only encrypt 245 characters at a time which means encrypting of large files will be painfully slow.

web/index.php

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535

3636
//let's filter out the hostname and get rid of every special char except for: . _ -
3737
$hostname = preg_replace("/[^a-zA-Z0-9\.\-_]+/", "", $hostname);
38-
echo json_encode(handleUpload($hostname));
38+
echo json_encode(handleUpload($hostname)).PHP_EOL;
3939
}
4040

4141

@@ -47,18 +47,25 @@ function handleUpload($hostname)
4747
if(isset($_FILES["file"]) && $_FILES["file"]["error"] == 0)
4848
{
4949
//target name of the backup is the date and the original extension
50-
$backupname = date("Y-m-d H.i").'.'.pathinfo($_FILES["file"]["name"], PATHINFO_EXTENSION);
50+
$backupname = date("Y-m-d_H.i").'.'.pathinfo($_FILES["file"]["name"], PATHINFO_EXTENSION);
5151
$path = ROOT.DS.'..'.DS.'data'.DS.$hostname.DS;
5252
if(!is_dir($path)) mkdir($path); //if the path doesn't exist yet, create it
5353

54-
// if the user wants to encrypt it
54+
// if the user wants to encrypt it using custom key
5555
if($_REQUEST['enc_key'] || $_REQUEST['pub_key'])
5656
{
5757
$backupname.='.enc';
5858
$e = new Encryption;
5959
if(!$e->encryptFile($_FILES["file"]["tmp_name"], ($_REQUEST['enc_key']?:$_REQUEST['pub_key']), $path.$backupname,($_REQUEST['pub_key']?true:false)))
6060
return ['status'=>'error','reason'=>'Failed to encrypt. Is the Key valid?'];
6161
}
62+
else if(defined('ENCRYPTION_AGE_SSH_PUBKEY') || defined('ENCRYPTION_AGE_PUBKEY') && (new Encryption)->checkAge()) //if the user wants to encrypt it using the predefined key
63+
{
64+
$backupname.='.age';
65+
$e = new Encryption;
66+
if(!$e->encryptAge($_FILES["file"]["tmp_name"], $path.$backupname))
67+
return ['status'=>'error','reason'=>'Failed to encrypt. Is the Key valid?'];
68+
}
6269
else
6370
move_uploaded_file($_FILES["file"]["tmp_name"], $path.$backupname);
6471

web/lib/encryption.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,4 +110,41 @@ function decryptFile($source, $key, $dest,$pubkey=false)
110110

111111
return $error ? false : $dest;
112112
}
113+
114+
function checkAge()
115+
{
116+
$age = shell_exec('which age');
117+
if($age)
118+
return true;
119+
return false;
120+
}
121+
122+
function encryptAge($source, $dest){
123+
if(!$this->checkAge())
124+
throw new Exception('age not found');
125+
$pubkey = defined('ENCRYPTION_AGE_SSH_PUBKEY') && ENCRYPTION_AGE_SSH_PUBKEY != '' ? ENCRYPTION_AGE_SSH_PUBKEY : false;
126+
$sshpubkey = defined('ENCRYPTION_AGE_PUBKEY') && ENCRYPTION_AGE_PUBKEY != '' ? ENCRYPTION_AGE_PUBKEY : false;
127+
128+
if(!$pubkey && !$sshpubkey)
129+
throw new Exception('No pubkeys configured');
130+
131+
$cmd = ['age'];
132+
133+
if($pubkey)
134+
$cmd[] = '-r '.escapeshellarg(ENCRYPTION_AGE_SSH_PUBKEY);
135+
if($sshpubkey)
136+
$cmd[] = '-r '.escapeshellarg(ENCRYPTION_AGE_PUBKEY);
137+
138+
$cmd[] = '-o '.escapeshellarg($dest);
139+
$cmd[] = escapeshellarg($source);
140+
141+
142+
$cmd = implode(' ',$cmd);
143+
144+
shell_exec($cmd);
145+
146+
if(file_exists($dest) && filesize($dest) > 0)
147+
return true;
148+
return false;
149+
}
113150
}

0 commit comments

Comments
 (0)