diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6f9d69d --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +# Vagrant folder +.vagrant/ + +# phpstorm project files +.idea/ +/nbproject/ +# local config files +local.env + +vendor/ +*.aes diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ef2ebaa --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +FROM alpine:3.11 + +RUN apk update \ + && apk add --no-cache \ + rsyslog rsyslog-tls \ + ca-certificates openssl \ + bash \ + && update-ca-certificates + +COPY dockerbuild/rsyslog.conf /etc/rsyslog.conf + +RUN wget https://raw.githubusercontent.com/silinternational/runny/0.2/runny \ + -O /usr/local/bin/runny \ + && chmod +x /usr/local/bin/runny + +COPY application/ /data/ +WORKDIR /data + +ENTRYPOINT ["./entrypoint.sh"] +CMD ["crond -f"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9649ce0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 SIL International + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..a3acab8 --- /dev/null +++ b/README.md @@ -0,0 +1,41 @@ +# docker-restic-backup +Docker image to initialize, backup to, and restore from a Restic repository on AWS S3. + +## How to use it +1. Create an S3 bucket to hold your backups +2. Supply all appropriate environment variables +3. Initialize the Restic repository on S3 to hold the backups (one-time only) +4. Run a backup and check your bucket for that backup + +### Environment variables +1. `AWS_ACCESS_KEY_ID` - used for S3 interactions + +2. `AWS_SECRET_ACCESS_KEY` - used for S3 interactions + +3. `AWS_DEFAULT_REGION` - AWS default region for S3 bucket + +4. `CRON_SCHEDULE="0 2 * * *"` - Schedule for cron job, _defaults to every day at 2:00 AM_ [syntax reference](https://en.wikipedia.org/wiki/Cron) + +5. `FSBACKUP_MODE=[init|backup|restore]` - + +6. `LOGENTRIES_KEY` - (optional) If provided, the image will send command output to syslog with priority `user.info`. + +7. `RESTIC_BACKUP_ARGS` - additional arguments to pass to 'restic backup' command + +8. `RESTIC_FORGET_ARGS` - additional arguments to pass to 'restic forget --prune' command (e.g., --keep-daily 7 --keep-weekly 5 --keep-monthly 3 --keep-yearly 2) + +9. `RESTIC_HOST` - hostname to record as source of backup + +10. `RESTIC_PASSWORD` - password for the Restic repository + +11. `RESTIC_REPOSITORY` - Restic repository location (e.g., 's3:s3.amazonaws.com/bucketname/restic') + +12. `SOURCE_PATH` - full path to the source directory to be backed up + +13. `TARGET_PATH` - full path to the target directory to be restored to (usually the same as the SOURCE\_PATH) + +It's recommended that your S3 bucket **NOT** have versioning turned on. +Old versions of Restic's repository files are not useful. + +## Docker Hub +This image is built automatically on Docker Hub as [silintl/restic-backup-restore](https://hub.docker.com/r/silintl/restic-backup-restore/) diff --git a/application/backup.sh b/application/backup.sh new file mode 100755 index 0000000..325faa1 --- /dev/null +++ b/application/backup.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env sh + +logger -p user.info "Started backup..." + +start=$(date +%s) +runny $(/usr/local/bin/restic backup ${RESTIC_BACKUP_ARGS} ${SOURCE_PATH}) +end=$(date +%s) + +logger -p user.info "Backup completed in $(expr ${end} - ${start}) seconds." + +start=$(date +%s) +runny $(/usr/local/bin/restic forget ${RESTIC_FORGET_ARGS} --prune) +end=$(date +%s) + +logger -p user.info "Backup pruning completed in $(expr ${end} - ${start}) seconds." + +start=$(date +%s) +runny $(/usr/local/bin/restic check) +end=$(date +%s) + +logger -p user.info "Repository check completed in $(expr ${end} - ${start}) seconds." + +logger -p user.info "...completed backup." diff --git a/application/entrypoint.sh b/application/entrypoint.sh new file mode 100755 index 0000000..0afd7ec --- /dev/null +++ b/application/entrypoint.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env sh + +hostname ${RESTIC_HOST} + +if [ "${LOGENTRIES_KEY}" ]; then + sed -i /etc/rsyslog.conf -e "s/LOGENTRIESKEY/${LOGENTRIES_KEY}/" + rsyslogd + sleep 10 # ensure rsyslogd is running before we may need to send logs to it +else + logger -p user.error "Missing LOGENTRIES_KEY environment variable" +fi + +# default to every day at 2 am when no schedule is provided +echo "${CRON_SCHEDULE:=0 2 * * *} runny /data/${FSBACKUP_MODE}.sh" >> /etc/crontabs/root + +runny $1 diff --git a/application/init.sh b/application/init.sh new file mode 100755 index 0000000..af99a37 --- /dev/null +++ b/application/init.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env sh + +logger -p user.info "Started Restic repository initialization..." + +start=$(date +%s) +runny $(/usr/local/bin/restic init) +end=$(date +%s) + +logger -p user.info "Repository initialization completed in $(expr ${end} - ${start}) seconds." + +logger -p user.info "...completed repository initialization." diff --git a/application/restore.sh b/application/restore.sh new file mode 100755 index 0000000..319f75b --- /dev/null +++ b/application/restore.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env sh + +logger -p user.info "Restoring ${RESTIC_RESTORE_ID}..." + +start=$(date +%s) +runny $(/usr/local/bin/restic restore ${RESTIC_RESTORE_ID} --target ${TARGET_PATH}) +end=$(date +%s) + +logger -p user.info "Restoration completed in $(expr ${end} - ${start}) seconds." diff --git a/dockerbuild/rsyslog.conf b/dockerbuild/rsyslog.conf new file mode 100644 index 0000000..2a2b661 --- /dev/null +++ b/dockerbuild/rsyslog.conf @@ -0,0 +1,16 @@ +# if you experience problems, check: +# http://www.rsyslog.com/troubleshoot + +$ModLoad imuxsock # provides support for local system logging (e.g. via logger command) + +# +# Configure TLS (logentries-specific example: https://docs.logentries.com/docs/rsyslog/) +# +$DefaultNetstreamDriverCAFile /etc/ssl/certs/ca-certificates.crt +$ActionSendStreamDriver gtls +$ActionSendStreamDriverMode 1 +$ActionSendStreamDriverAuthMode x509/name +$ActionSendStreamDriverPermittedPeer *.logentries.com + +$template LogentriesFormat,"LOGENTRIESKEY %msg%\n" +*.emerg,*.alert,*.crit,*.err,*.warning,user.* @@data.logentries.com:443;LogentriesFormat diff --git a/local.env.dist b/local.env.dist new file mode 100644 index 0000000..2a2b9f0 --- /dev/null +++ b/local.env.dist @@ -0,0 +1,13 @@ +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_DEFAULT_REGION= +CRON_SCHEDULE= +FSBACKUP_MODE= +LOGENTRIES_KEY= +RESTIC_BACKUP_ARGS= +RESTIC_FORGET_ARGS= +RESTIC_HOST= +RESTIC_PASSWORD= +RESTIC_REPOSITORY= +SOURCE_PATH= +TARGET_PATH=