Skip to content

Commit

Permalink
added support for age encryption for automatic encryption against ssh…
Browse files Browse the repository at this point in the history
… or age keys after upload
  • Loading branch information
geek-at committed Dec 7, 2024
1 parent 45063ca commit 126f804
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 6 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<div align="center">


![](https://img.shields.io/badge/php-7.4%2B-brightgreen.svg)
![](https://img.shields.io/badge/php-8.3%2B-brightgreen.svg)
[![Apache License](https://img.shields.io/badge/license-Apache-brightgreen.svg?style=flat)](https://github.com/hascheksolutions/backupdrop/blob/master/LICENSE)
![HitCount](http://hits.dwyl.com/hascheksolutions/backupdrop.svg)
[![](https://img.shields.io/github/stars/hascheksolutions/backupdrop.svg?label=Stars&style=social)](https://github.com/hascheksolutions/backupdrop)
Expand Down
6 changes: 6 additions & 0 deletions config/example.config.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
// copy this file to config.inc.php
// and edit to your needs


// AGE encryption settings
// More info on age encryption: https://github.com/FiloSottile/age
define('ENCRYPTION_AGE_SSH_PUBKEY',''); // Enter your SSH public key here to automatically encrypt all uploads
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

// global settings for retention and version control
// 0 means unlimited
define('KEEP_N_BACKUPS',0); // How many uploads will be saved. Oldest one will be deleted if this number is surpassed
Expand Down
2 changes: 1 addition & 1 deletion docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
FROM alpine:3.20

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

RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/bin --filename=composer
RUN mkdir -p /var/www/backupdrop
Expand Down
3 changes: 3 additions & 0 deletions docker/rootfs/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ nginx
_buildConfig() {
echo "<?php"

echo "define('ENCRYPTION_AGE_SSH_PUBKEY','${ENCRYPTION_AGE_SSH_PUBKEY:-}');"
echo "define('ENCRYPTION_AGE_PUBKEY','${ENCRYPTION_AGE_PUBKEY:-}');"

echo "define('S3_BUCKET','${S3_BUCKET:-}');"
echo "define('S3_ACCESS_KEY','${S3_ACCESS_KEY:-}');"
echo "define('S3_SECRET_KEY','${S3_SECRET_KEY:-}');"
Expand Down
22 changes: 21 additions & 1 deletion rtfm/encryption.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,27 @@

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

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).
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).

## Method 1: BackupDrop builtin age support
To use age in BackupDrop you only have to set one of two (or both) configuration options in the config file:

- ENCRYPTION_AGE_SSH_PUBKEY
- ENCRYPTION_AGE_PUBKEY

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.

### SSH key based encryption
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.

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`

Read more about age and SSH key based encryption [here](https://github.com/FiloSottile/age?tab=readme-ov-file#ssh-keys)

### age public key encryption
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.

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`

## Method 1: Encrypt using Public Key
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.
Expand Down
13 changes: 10 additions & 3 deletions web/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@

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


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

// if the user wants to encrypt it
// if the user wants to encrypt it using custom key
if($_REQUEST['enc_key'] || $_REQUEST['pub_key'])
{
$backupname.='.enc';
$e = new Encryption;
if(!$e->encryptFile($_FILES["file"]["tmp_name"], ($_REQUEST['enc_key']?:$_REQUEST['pub_key']), $path.$backupname,($_REQUEST['pub_key']?true:false)))
return ['status'=>'error','reason'=>'Failed to encrypt. Is the Key valid?'];
}
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
{
$backupname.='.age';
$e = new Encryption;
if(!$e->encryptAge($_FILES["file"]["tmp_name"], $path.$backupname))
return ['status'=>'error','reason'=>'Failed to encrypt. Is the Key valid?'];
}
else
move_uploaded_file($_FILES["file"]["tmp_name"], $path.$backupname);

Expand Down
37 changes: 37 additions & 0 deletions web/lib/encryption.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,4 +110,41 @@ function decryptFile($source, $key, $dest,$pubkey=false)

return $error ? false : $dest;
}

function checkAge()
{
$age = shell_exec('which age');
if($age)
return true;
return false;
}

function encryptAge($source, $dest){
if(!$this->checkAge())
throw new Exception('age not found');
$pubkey = defined('ENCRYPTION_AGE_SSH_PUBKEY') && ENCRYPTION_AGE_SSH_PUBKEY != '' ? ENCRYPTION_AGE_SSH_PUBKEY : false;
$sshpubkey = defined('ENCRYPTION_AGE_PUBKEY') && ENCRYPTION_AGE_PUBKEY != '' ? ENCRYPTION_AGE_PUBKEY : false;

if(!$pubkey && !$sshpubkey)
throw new Exception('No pubkeys configured');

$cmd = ['age'];

if($pubkey)
$cmd[] = '-r '.escapeshellarg(ENCRYPTION_AGE_SSH_PUBKEY);
if($sshpubkey)
$cmd[] = '-r '.escapeshellarg(ENCRYPTION_AGE_PUBKEY);

$cmd[] = '-o '.escapeshellarg($dest);
$cmd[] = escapeshellarg($source);


$cmd = implode(' ',$cmd);

shell_exec($cmd);

if(file_exists($dest) && filesize($dest) > 0)
return true;
return false;
}
}

0 comments on commit 126f804

Please sign in to comment.