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

✨ Allow for an asymmetric public key encryption #81

Merged
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
37 changes: 35 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ docker run -it \

### Advanced Usage

Example with different region, different S3 storage class, different signature version and call to S3-compatible service (different endpoint url)
Example with different region, different S3 storage class, different signature version and call to S3-compatible
service (different endpoint url)

```shell
docker run -it \
Expand All @@ -42,7 +43,8 @@ docker run -it \

### Encryption

You can optionally encrypt your backup using GnuPG. To do so, set ENCRYPTION_KEY.
You can optionally encrypt your backup using GnuPG. To do so, set ENCRYPTION_KEY. This would encrypt the backup with the
passphrase "your_secret_passphrase". The cypher algorithm used is AES256.

```shell
docker run -it \
Expand All @@ -54,6 +56,37 @@ docker run -it \
-v /path/to/backup:/backup dokku/s3backup
```

You can also use a GPG public key to encrypt the backup. To do so, set ENCRYPTION_KEY to the public key. This would
encrypt the backup with the public key. **The backup can only be decrypted with the corresponding private key**, making
it impossible to encrypt your data even if the backups and all the configuration files are compromised.

```shell
docker run -it \
-e AWS_ACCESS_KEY_ID=ID \
-e AWS_SECRET_ACCESS_KEY=KEY \
-e BUCKET_NAME=backups \
-e BACKUP_NAME=backup \
-e ENCRYPT_WITH_PUBLIC_KEY_ID=public_key_id \
-v /path/to/backup:/backup dokku/s3backup
```

In the above command, replace `public_key_id` with the ID (or, even better, the fingerprint) of your GPG public key. The
backup will be encrypted using this
public key and can only be decrypted with the corresponding private key. Please note that the public key must be
available on the keyserver specified by the KEYSERVER environment variable. By default, this is set
to `hkp://keyserver.ubuntu.com` and can be overridden by setting the KEYSERVER environment variable:

```shell
docker run -it \
-e AWS_ACCESS_KEY_ID=ID \
-e AWS_SECRET_ACCESS_KEY=KEY \
-e BUCKET_NAME=backups \
-e BACKUP_NAME=backup \
-e ENCRYPT_WITH_PUBLIC_KEY_ID=public_key_id \
-e KEYSERVER=hkp://pgp.mit.edu \
-v /path/to/backup:/backup dokku/s3backup
```

## Building

First, build the image.
Expand Down
98 changes: 74 additions & 24 deletions backup.sh
Original file line number Diff line number Diff line change
@@ -1,48 +1,98 @@
#!/bin/bash

set -eo pipefail
[[ -n "$TRACE" ]] && set -x

# Check if backup directory exists
if [[ ! -d "/backup" ]]; then
echo "Please mount a directory to backup with -v /backup:/backup"
exit 1
fi

#Amazon S3 info
AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID:-null}
AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY:-null}
AWS_DEFAULT_REGION=${AWS_DEFAULT_REGION:-null}
S3_STORAGE_CLASS=${S3_STORAGE_CLASS:-STANDARD}
# Set default values for Amazon S3 info
AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-null}"
AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-null}"
AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION:-null}"

BACKUP_NAME="${BACKUP_NAME:-backup}"
BUCKET_NAME="${BUCKET_NAME:-null}"

BACKUP_NAME=${BACKUP_NAME:-backup}
BUCKET_NAME=${BUCKET_NAME:-null}
# Set default keyserver (Ubuntu keyserver)
KEYSERVER="${KEYSERVER:-hkp://keyserver.ubuntu.com}"

#System info
TIMESTAMP=$(date -u "+%Y-%m-%d-%H-%M-%S")
# Set timestamp for backup
TIMESTAMP="$(date -u "+%Y-%m-%d-%H-%M-%S")"

### Build endpoint parameter if endpoint given
# Build endpoint parameter if endpoint given
if [[ -n "$ENDPOINT_URL" ]]; then
ENDPOINT_URL_PARAMETER="--endpoint-url=$ENDPOINT_URL"
fi

### Setup AWS signature version if specified
# Add the StorageClass parameter if specified
if [[ -n "$S3_STORAGE_CLASS" ]]; then
S3_STORAGE_CLASS_PARAMETER="--storage-class=$S3_STORAGE_CLASS"
fi

# Setup AWS signature version if specified
if [[ -n "$AWS_SIGNATURE_VERSION" ]]; then
aws configure set default.s3.signature_version "$AWS_SIGNATURE_VERSION"
fi

### Run backup to Amazon S3 Bucket
# Set target directory for backup
TARGET="backup/"
if [[ -n "$ENCRYPTION_KEY" ]]; then
# shellcheck disable=SC2086
/bin/tar -czf - "$TARGET" | gpg --batch --no-tty -q -c --passphrase "$ENCRYPTION_KEY" | aws $ENDPOINT_URL_PARAMETER s3 cp - "s3://$BUCKET_NAME/$BACKUP_NAME-$TIMESTAMP.tgz.gpg" --storage-class $S3_STORAGE_CLASS
else
# shellcheck disable=SC2086
/bin/tar -czf - "$TARGET" | aws $ENDPOINT_URL_PARAMETER s3 cp - "s3://$BUCKET_NAME/$BACKUP_NAME-$TIMESTAMP.tgz" --storage-class $S3_STORAGE_CLASS
fi

# shellcheck disable=SC2181
if [[ "$?" -eq "0" ]]; then
echo "$TIMESTAMP: The backup for $BACKUP_NAME finished successfully."
# Function to create a tar archive of the target directory
create_tar_archive() {
tar --create --gzip --file - "$TARGET"
}

# Function to encrypt the input stream
encrypt_stream() {
if [[ "$1" == "public_key" ]]; then
gpg --batch --no-tty --quiet --encrypt --always-trust --recipient "$ENCRYPT_WITH_PUBLIC_KEY_ID"
elif [[ "$1" == "encryption_key" ]]; then
gpg --batch --no-tty --quiet --symmetric --cipher-algo AES256 --passphrase "$ENCRYPTION_KEY"
else
cat
fi
}

# Function to upload backup to S3
upload_to_s3() {
if [[ "$1" == "encrypted" ]]; then
# shellcheck disable=SC2086
aws $ENDPOINT_URL_PARAMETER s3 cp - "s3://$BUCKET_NAME/$BACKUP_NAME-$TIMESTAMP.tgz.gpg" $S3_STORAGE_CLASS_PARAMETER
else
# shellcheck disable=SC2086
aws $ENDPOINT_URL_PARAMETER s3 cp - "s3://$BUCKET_NAME/$BACKUP_NAME-$TIMESTAMP.tgz" $S3_STORAGE_CLASS_PARAMETER
fi
}

# Perform backup based on encryption method
if [[ -n "$ENCRYPT_WITH_PUBLIC_KEY_ID" ]]; then
if gpg --quiet --keyserver "$KEYSERVER" --recv-keys "$ENCRYPT_WITH_PUBLIC_KEY_ID"; then
if create_tar_archive | encrypt_stream "public_key" | upload_to_s3 "encrypted"; then
echo "$TIMESTAMP: The backup for $BACKUP_NAME finished successfully."
else
echo "Backup of $TARGET has failed. Please investigate the issue."
exit 1
fi
else
echo "Error: Failed to retrieve the public key from the keyserver."
exit 1
fi
elif [[ -n "$ENCRYPTION_KEY" ]]; then
if create_tar_archive | encrypt_stream "encryption_key" | upload_to_s3 "encrypted"; then
echo "$TIMESTAMP: The backup for $BACKUP_NAME finished successfully."
else
echo "Backup of $TARGET has failed. Please investigate the issue."
exit 1
fi
else
echo "Backup of $TARGET has failed. Please look into this and find out what went wrong"
if create_tar_archive | encrypt_stream "no_encryption" | upload_to_s3 "unencrypted"; then
echo "$TIMESTAMP: The backup for $BACKUP_NAME finished successfully."
else
echo "Backup of $TARGET has failed. Please investigate the issue."
exit 1
fi
fi
### Finish Amazon backup