Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize storage and speed when making an encrypted backup #1825

Open
wants to merge 1 commit into
base: v8
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions docs/advanced-usage/encrypt-backup-archives.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,6 @@ By default you only have to define the `BACKUP_ARCHIVE_PASSWORD` environment var

If you want to customize this you can configure the `backup.backup.password` and `backup.backup.encryption` keys in your `config/backup.php` file.

The whole encryption is done with an event listener.
The `\Spatie\Backup\Listeners\EncryptBackupArchive` listener is attached to the `\Spatie\Backup\Events\BackupZipWasCreated` event.
The listener is added to the event when both required config keys are not `null`.
You are free to add this listener your own or override it.

It's important to try this workflow and also to decrypt a backup archive.
So you know that it works and you have a working backup restore solution.

Expand Down
4 changes: 0 additions & 4 deletions src/BackupServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,6 @@ public function configurePackage(Package $package): void
public function packageBooted()
{
$this->app['events']->subscribe(EventHandler::class);

if (EncryptBackupArchive::shouldEncrypt()) {
Event::listen(BackupZipWasCreated::class, EncryptBackupArchive::class);
}
}

public function packageRegistered()
Expand Down
67 changes: 0 additions & 67 deletions src/Listeners/EncryptBackupArchive.php

This file was deleted.

6 changes: 1 addition & 5 deletions src/Tasks/Backup/BackupJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -228,11 +228,7 @@ protected function createZipContainingEveryFileInManifest(Manifest $manifest): s

consoleOutput()->info("Created zip containing {$zip->count()} files and directories. Size is {$zip->humanReadableSize()}");

if ($this->sendNotifications) {
$this->sendNotification(new BackupZipWasCreated($pathToZip));
} else {
app()->call('\Spatie\Backup\Listeners\EncryptBackupArchive@handle', ['event' => new BackupZipWasCreated($pathToZip)]);
}
$this->sendNotification(new BackupZipWasCreated($pathToZip));

return $pathToZip;
}
Expand Down
27 changes: 27 additions & 0 deletions src/Tasks/Backup/Encryption.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace Spatie\Backup\Tasks\Backup;

class Encryption
{
protected string $password;

protected int $method;

public function __construct(string $password, int $method)
{
$this->password = $password;

$this->method = $method;
}

public function getPassword(): string
{
return $this->password;
}

public function getMethod(): int
{
return $this->method;
}
}
49 changes: 49 additions & 0 deletions src/Tasks/Backup/Zip.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ class Zip

protected string $pathToZip;

protected ?Encryption $encryption = null;

public static function createForManifest(Manifest $manifest, string $pathToZip): self
{
$relativePath = config('backup.backup.source.files.relative_path') ?
Expand All @@ -27,6 +29,8 @@ public static function createForManifest(Manifest $manifest, string $pathToZip):
$zip->add($file, self::determineNameOfFileInZip($file, $pathToZip, $relativePath));
}

$zip->encrypt();

$zip->close();

return $zip;
Expand Down Expand Up @@ -87,6 +91,11 @@ public function close(): void
$this->zipFile->close();
}

public function setPassword(string $password): void
{
$this->zipFile->setPassword($password);
}

public function add(string|iterable $files, ?string $nameInZip = null): self
{
if (is_array($files)) {
Expand Down Expand Up @@ -126,4 +135,44 @@ public function count(): int
{
return $this->fileCount;
}

public function encrypt()
{
$this->loadEncryption();

if ($this->getEncryption()) {
$this->setPassword($this->getEncryption()->getPassword());

foreach (range(0, $this->zipFile->numFiles - 1) as $i) {
$this->zipFile->setEncryptionIndex($i, $this->getEncryption()->getMethod());
}
}
}

public function getEncryption(): ?Encryption
{
return $this->encryption;
}

public function loadEncryption()
{
$password = config('backup.backup.password');
$method = config('backup.backup.encryption');

if ($method === 'default') {
$method = defined("\ZipArchive::EM_AES_256")
? ZipArchive::EM_AES_256
: null;
}

if ($password === null) {
return false;
}

if (! is_int($method)) {
return false;
}

$this->encryption = new Encryption($password, $method);
}
}
68 changes: 66 additions & 2 deletions tests/Commands/BackupCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@

$testFiles = [
'.dotfile',
'archive.zip',
'1Mb.file',
'directory1/',
'directory1/directory1/',
Expand Down Expand Up @@ -358,8 +357,73 @@
app()['db']->disconnect();
});

it('keeps archive unencrypted without password', function () {
config()->set('backup.backup.password', null);
config()->set('backup.backup.source.files.include', [$this->getStubDirectory()]);
config()->set('backup.backup.source.files.relative_path', $this->getStubDirectory());

$this->artisan('backup:run --only-files')->assertExitCode(0);
Storage::disk('local')->assertExists($this->expectedZipPath);

$zip = new ZipArchive();
$zip->open(Storage::disk('local')->path($this->expectedZipPath));

$this->assertEncryptionMethod($zip, ZipArchive::EM_NONE);

$this->assertTrue($zip->extractTo(Storage::disk('local')->path('temp/extraction')));
$this->assertValidExtractedFiles();

$zip->close();
});

/**
* @param int $algorithm
*/
it('encrypts archive with password', function (int $algorithm) {
config()->set('backup.backup.password', $this->fakePassword());
config()->set('backup.backup.encryption', $algorithm);
config()->set('backup.backup.source.files.include', [$this->getStubDirectory()]);
config()->set('backup.backup.source.files.relative_path', $this->getStubDirectory());

$this->artisan('backup:run --only-files')->assertExitCode(0);

Storage::disk('local')->assertExists($this->expectedZipPath);

$zip = new ZipArchive();
$zip->open(Storage::disk('local')->path($this->expectedZipPath));

$this->assertEncryptionMethod($zip, $algorithm);

$zip->setPassword($this->fakePassword());
$this->assertTrue($zip->extractTo(Storage::disk('local')->path('temp/extraction')));
$this->assertValidExtractedFiles();

$zip->close();
})->with([
[ZipArchive::EM_AES_128],
[ZipArchive::EM_AES_192],
[ZipArchive::EM_AES_256],
]);

it('can not open encrypted archive without password', function () {
config()->set('backup.backup.password', $this->fakePassword());

$this->artisan('backup:run --only-files')->assertExitCode(0);

Storage::disk('local')->assertExists($this->expectedZipPath);

$zip = new ZipArchive();
$zip->open(Storage::disk('local')->path($this->expectedZipPath));

$this->assertEncryptionMethod($zip, ZipArchive::EM_AES_256);

expect($zip->extractTo(Storage::disk('local')->path('temp/extraction')))->toBeFalse();

$zip->close();
});

it('will encrypt backup when notifications are disabled', function () {
config()->set('backup.backup.password', '24dsjF6BPjWgUfTu');
config()->set('backup.backup.password', $this->fakePassword());
config()->set('backup.backup.source.databases', ['db1']);

$this->artisan('backup:run --disable-notifications --only-db --db-name=db1 --only-to-disk=local')->assertExitCode(0);
Expand Down
4 changes: 0 additions & 4 deletions tests/FileSelectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
'.dot',
'.dot/file1.txt',
'.dotfile',
'archive.zip',
'1Mb.file',
'directory1',
'directory1/directory1',
Expand Down Expand Up @@ -43,7 +42,6 @@
'.dot',
'.dot/file1.txt',
'.dotfile',
'archive.zip',
'1Mb.file',
'directory2',
'directory2/directory1',
Expand All @@ -69,7 +67,6 @@
$testFiles = getTestFiles([
'.dot',
'.dotfile',
'archive.zip',
'1Mb.file',
'directory1',
'directory1/file2.txt',
Expand Down Expand Up @@ -113,7 +110,6 @@
'.dot',
'.dot/file1.txt',
'.dotfile',
'archive.zip',
'1Mb.file',
'directory1',
'directory1/file1.txt',
Expand Down
Loading