├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── build.sh └── remote-backup ├── Dockerfile ├── config.json └── run.sh /.gitignore: -------------------------------------------------------------------------------- 1 | local_build.sh 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | services: 3 | - docker 4 | script: 5 | - "$TRAVIS_BUILD_DIR/build.sh" 6 | env: 7 | global: 8 | secure: I1qaPNCHzZ0rntK1/aMMSCI50zhIdHO+V2UPwimUHmlQ8YdW1dUnuictFFoGEbp2S7owub9U4cwm5LHp8VjtrnDxoDEBuzb50RMTQ8vNWE4SAILawe9TWKfHQKqgCgONQsv9O4Bh8moqwDD+PrqnoGvqEsRjiS6Icx8jk1zCvy7WDzpgnZIHIzytAudRTNwVb/nIJWGMfG29ekxqLgX1YXQ6XsZfQm+JSt8a8dGLdYoYQZRbrriznanYkeMrBanwkKQgbmA1MirIwfcprvnXad1aX8l6/uKomvehc1PbUH6DXUqq8lzuu42gF+J9W3aL4YtbWYtzOItq2Hi0nnj26EvUyn1yIGzfCXv4j3eNA6cqplPKePZ9dYREx0bEiQP412T6w1WviRrhB4THSeCmn6HCkb/k2quTX4vLT1lQWOv4CRe0Qp5kHExbe2kGREJONiUsXU/6zKtT4S+iF9g5EalCaG8beFIwYllkbcSIsle/mfjHKTvwwQ8vI0cnRb7aKLI1pLgb6fQVIRN5ia+fwOvd/S/phy30wAzqtAjuxehbJ8dmnaS0Q21CRUmMAsRJ09x4KUTRMg0hBVib7JJ6jPQIvGgyILe4aJtBFZ5upxwPtk3eEc29RV/FbgJzPa7419boPflIpPY20a+VkfUpSjD4UcS/Xvd+6bjOecT9Fh4= 9 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog][keep-a-changelog] this project adheres to [Semantic Versioning][semantic-versioning]. 5 | 6 | ## [v0.3.0] (2018-03-12) 7 | 8 | [Full Changelog](https://github.com/mr-bjerre/hassio-remote-backup/compare/v0.2.1...v0.3.0) 9 | 10 | ### Added 11 | 12 | - New input `keep_local_backup` to control how many local snapshots there should be preserved. 13 | 14 | ## [v0.2.1] (2018-03-11) 15 | 16 | [Full Changelog](https://github.com/mr-bjerre/hassio-remote-backup/compare/v0.2.0...v0.2.1) 17 | 18 | ### Fixed 19 | 20 | - `config.json` in the [addons repository][addons-repo] was not aligned with the updates from v0.1.0 to v0.2.0. All backups created with v0.2.0 is password protected with the password _null_. 21 | 22 | 23 | ## [v0.2.0] (2018-03-07) 24 | 25 | [Full Changelog](https://github.com/mr-bjerre/hassio-remote-backup/compare/v0.1.0...v0.2.0) 26 | 27 | ### Added 28 | 29 | - Possibilty to contain the backup in a password protected zip file. 30 | 31 | ## [v0.1.0] (2018-03-06) 32 | 33 | ### Added 34 | 35 | - Initial release 36 | 37 | [keep-a-changelog]: http://keepachangelog.com/en/1.0.0/ 38 | [semantic-versioning]: http://semver.org/spec/v2.0.0.html 39 | [addons-repo]: https://github.com/mr-bjerre/hassio-addons/blob/master/remote-backup/config.json 40 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Nicolai Bjerre Pedersen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Remote Backup 3 | 4 | [![GitHub Release][releases-shield]][releases] 5 | [![Build Status][travis-build-shield]][travis-build] 6 | [![GitHub license][license-shield]](LICENSE.md) 7 | 8 | > Automatically create Hass.io snapshots to remote server location using `SCP`. 9 | 10 |
11 | 12 | ## Table of Contents 13 | 14 | * [About](#about) 15 | * [Installation](#installation) 16 | * [Configuration](#configuration) 17 | * [Example](#example) 18 | * [Changelog & Releases](#changelog) 19 | * [Docker status](#docker) 20 | 21 | ## About 22 | 23 | When the add-on is started the following happens: 24 | 1. Snapshot are being created locally with a timestamp name, e.g. 25 | *Automatic backup 2018-03-04 04:00*. 26 | 1. The snapshot are copied to the specified remote location using `SCP`. 27 | 1. The local backup are removed locally again. 28 | 29 | _Note_ the filenames of the backup are given by their assigned slug. 30 | 31 | ## Installation 32 | 33 | 1. Add the add-ons repository to your Hass.io instance: `https://github.com/overkill32/hassio-addons`. 34 | 1. Install the Remote Backup add-on. 35 | 1. Configure the add-on with your SSH credentials and desired output directory 36 | (see configuration below). 37 | 38 | See my [repository of addons][hassio-addons] for more information. 39 | 40 | ## Configuration 41 | 42 | |Parameter|Required|Description| 43 | |---------|--------|-----------| 44 | |`ssh_host`|Yes|The hostname/url to the remote server.| 45 | |`ssh_port`|Yes|The port to use to `SCP` on to the server.| 46 | |`ssh_user`|Yes|Username to use for `SCP`.| 47 | |`ssh_key`|Yes|The ssh key to use. Not that it should *NOT* be password protected.| 48 | |`remote_directory`|Yes|The directory to put the backups on the remote server.| 49 | |`zip_password`|No|If set then the backup will be contained in a password protected zip| 50 | |`keep_local_backup`|No|Control how many local backups you want to preserve. Default (`""`) is to keep no local backups created from this addon. If `all` then all loocal backups will be preserved. A positive integer will determine how many of the latest backups will be preserved. Note this will delete other local backups created outside this addon. 51 | 52 | ## Example: daily backups at 4 AM 53 | 54 | Personally I've added the following automation to make a daily backup. It is password-protected and the last two weeks of snapshots are kept locally as well. 55 | 56 | _configuration.yaml_ 57 | ```yaml 58 | automations: 59 | - alias: Daily Backup at 4 AM 60 | trigger: 61 | platform: time 62 | at: '4:00:00' 63 | action: 64 | - service: hassio.addon_start 65 | data: 66 | addon: ce20243c_remote_backup 67 | ``` 68 | 69 | _Add-on configuration_: 70 | ```json 71 | { 72 | "ssh_host": "192.168.1.2", 73 | "ssh_port": 22, 74 | "ssh_user": "root", 75 | "ssh_key": [ 76 | "-----BEGIN RSA PRIVATE KEY-----", 77 | "MIICXAIBAAKBgQDTkdD4ya/Qxz5xKaKojVIOVWjyeyEoEuAafAvYvppqmaBhyh4N", 78 | "5av4i87y8tdGusdq7V0Zj0+js4jEdvJRDrXJBrp1neLfsjkF6t1XLfrA51Ll9SXF", 79 | "...", 80 | "X+6r/gTvUEQv1ufAuUE5wKcq9FsbnTa3FOF0PdQDWl0=", 81 | "-----END RSA PRIVATE KEY-----" 82 | ], 83 | "remote_directory": "~/hassio-backups", 84 | "zip_password": "password_protect_it", 85 | "keep_local_backup": "14" 86 | } 87 | ``` 88 | 89 | **Note**: _This is just an example, don't copy and past it! Create your own!_ 90 | 91 | ## Changelog & Releases 92 | 93 | This repository keeps a [change log](CHANGELOG.md). The format of the log 94 | is based on [Keep a Changelog][keepchangelog]. 95 | 96 | Releases are based on [Semantic Versioning][semver], and use the format 97 | of ``MAJOR.MINOR.PATCH``. In a nutshell, the version will be incremented 98 | based on the following: 99 | 100 | - ``MAJOR``: Incompatible or major changes. 101 | - ``MINOR``: Backwards-compatible new features and enhancements. 102 | - ``PATCH``: Backwards-compatible bugfixes and package updates. 103 | 104 | 105 | ## Docker status 106 | 107 | [![Docker Architecture][armhf-arch-shield]][armhf-dockerhub] 108 | [![Docker Version][armhf-version-shield]][armhf-microbadger] 109 | [![Docker Layers][armhf-layers-shield]][armhf-microbadger] 110 | [![Docker Pulls][armhf-pulls-shield]][armhf-dockerhub] 111 | 112 | [![Docker Architecture][aarch64-arch-shield]][aarch64-dockerhub] 113 | [![Docker Version][aarch64-version-shield]][aarch64-microbadger] 114 | [![Docker Layers][aarch64-layers-shield]][aarch64-microbadger] 115 | [![Docker Pulls][aarch64-pulls-shield]][aarch64-dockerhub] 116 | 117 | [![Docker Architecture][amd64-arch-shield]][amd64-dockerhub] 118 | [![Docker Version][amd64-version-shield]][amd64-microbadger] 119 | [![Docker Layers][amd64-layers-shield]][amd64-microbadger] 120 | [![Docker Pulls][amd64-pulls-shield]][amd64-dockerhub] 121 | 122 | [![Docker Architecture][i386-arch-shield]][i386-dockerhub] 123 | [![Docker Version][i386-version-shield]][i386-microbadger] 124 | [![Docker Layers][i386-layers-shield]][i386-microbadger] 125 | [![Docker Pulls][i386-pulls-shield]][i386-dockerhub] 126 | 127 | 128 | [aarch64-arch-shield]: https://img.shields.io/badge/architecture-aarch64-blue.svg 129 | [aarch64-dockerhub]: https://hub.docker.com/r/fixated/remote-backup-aarch64 130 | [aarch64-layers-shield]: https://images.microbadger.com/badges/image/fixated/remote-backup-aarch64.svg 131 | [aarch64-microbadger]: https://microbadger.com/images/fixated/remote-backup-aarch64 132 | [aarch64-pulls-shield]: https://img.shields.io/docker/pulls/fixated/remote-backup-aarch64.svg 133 | [aarch64-version-shield]: https://images.microbadger.com/badges/version/fixated/remote-backup-aarch64.svg 134 | [amd64-arch-shield]: https://img.shields.io/badge/architecture-amd64-blue.svg 135 | [amd64-dockerhub]: https://hub.docker.com/r/fixated/remote-backup-amd64 136 | [amd64-layers-shield]: https://images.microbadger.com/badges/image/fixated/remote-backup-amd64.svg 137 | [amd64-microbadger]: https://microbadger.com/images/fixated/remote-backup-amd64 138 | [amd64-pulls-shield]: https://img.shields.io/docker/pulls/fixated/remote-backup-amd64.svg 139 | [amd64-version-shield]: https://images.microbadger.com/badges/version/fixated/remote-backup-amd64.svg 140 | [armhf-arch-shield]: https://img.shields.io/badge/architecture-armhf-blue.svg 141 | [armhf-dockerhub]: https://hub.docker.com/r/fixated/remote-backup-armhf 142 | [armhf-layers-shield]: https://images.microbadger.com/badges/image/fixated/remote-backup-armhf.svg 143 | [armhf-microbadger]: https://microbadger.com/images/fixated/remote-backup-armhf 144 | [armhf-pulls-shield]: https://img.shields.io/docker/pulls/fixated/remote-backup-armhf.svg 145 | [armhf-version-shield]: https://images.microbadger.com/badges/version/fixated/remote-backup-armhf.svg 146 | [i386-arch-shield]: https://img.shields.io/badge/architecture-i386-blue.svg 147 | [i386-dockerhub]: https://hub.docker.com/r/fixated/remote-backup-i386 148 | [i386-layers-shield]: https://images.microbadger.com/badges/image/fixated/remote-backup-i386.svg 149 | [i386-microbadger]: https://microbadger.com/images/fixated/remote-backup-i386 150 | [i386-pulls-shield]: https://img.shields.io/docker/pulls/fixated/remote-backup-i386.svg 151 | [i386-version-shield]: https://images.microbadger.com/badges/version/fixated/remote-backup-i386.svg 152 | 153 | [license-shield]: https://img.shields.io/github/license/overkill32/hassio-remote-backup.svg 154 | [releases]: https://github.com/overkill32/hassio-remote-backup/releases 155 | [releases-shield]: https://img.shields.io/github/release/overkill32/hassio-remote-backup.svg 156 | [travis-build]: https://travis-ci.org/overkill32/hassio-remote-backup 157 | [travis-build-shield]: https://travis-ci.org/overkill32/hassio-remote-backup.svg?branch=master 158 | 159 | [keepchangelog]: http://keepachangelog.com/en/1.0.0/ 160 | [semver]: http://semver.org/spec/v2.0.0.html 161 | 162 | [hassio-addons]: https://github.com/overkill32/hassio-addons 163 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | set -ev 2 | docker login -u "$DOCKER_USERNAME" -p "$DOCKER_PASSWORD" 3 | 4 | if [ -z ${TRAVIS_TAG} ]; then 5 | echo "Untagged build found. Building to test." 6 | docker run -it --rm --privileged --name "${ADDON_NAME}" \ 7 | -v ~/.docker:/root/.docker \ 8 | -v "$(pwd)":/docker \ 9 | hassioaddons/build-env:latest \ 10 | --target "${ADDON_NAME}" \ 11 | --git \ 12 | --all \ 13 | --from "homeassistant/{arch}-base" \ 14 | --author "Nicolai Bjerre Pedersen " \ 15 | --doc-url "${GITHUB_URL}" \ 16 | --image "fixated/${ADDON_NAME}-{arch}" 17 | else 18 | echo "New git tagged build found. Building to distribute." 19 | docker run -it --rm --privileged --name "${ADDON_NAME}" \ 20 | -v ~/.docker:/root/.docker \ 21 | -v "$(pwd)":/docker \ 22 | hassioaddons/build-env:latest \ 23 | --target "${ADDON_NAME}" \ 24 | --git \ 25 | --all \ 26 | --push \ 27 | --from "homeassistant/{arch}-base" \ 28 | --author "Nicolai Bjerre Pedersen " \ 29 | --doc-url "${GITHUB_URL}" \ 30 | --image "fixated/${ADDON_NAME}-{arch}" 31 | fi 32 | -------------------------------------------------------------------------------- /remote-backup/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG BUILD_FROM 2 | FROM $BUILD_FROM 3 | 4 | # Add env 5 | ENV LANG C.UTF-8 6 | 7 | # Setup base 8 | RUN apk add --no-cache jq openssh-client zip 9 | 10 | # Hass.io CLI 11 | ARG BUILD_ARCH 12 | ARG CLI_VERSION 13 | RUN apk add --no-cache curl \ 14 | && curl -Lso /usr/bin/hassio https://github.com/home-assistant/hassio-cli/releases/download/1.2.1/hassio_${BUILD_ARCH} \ 15 | && chmod a+x /usr/bin/hassio 16 | 17 | # Copy data 18 | COPY run.sh / 19 | RUN chmod a+x /run.sh 20 | 21 | CMD [ "/run.sh" ] 22 | 23 | # Build arugments 24 | ARG BUILD_DATE 25 | ARG BUILD_REF 26 | ARG BUILD_VERSION 27 | 28 | # Labels 29 | LABEL \ 30 | io.hass.name="Remote Backup" \ 31 | io.hass.description="Automatically create Hass.io snapshots to remote server location using `SCP`." \ 32 | io.hass.arch="${BUILD_ARCH}" \ 33 | io.hass.type="addon" \ 34 | io.hass.version=${BUILD_VERSION} \ 35 | maintainer="Nicolai Bjerre Pedersen " \ 36 | org.label-schema.description="Automatically create Hass.io snapshots to remote server location using `SCP`." \ 37 | org.label-schema.build-date=${BUILD_DATE} \ 38 | org.label-schema.name="Remote Backup" \ 39 | org.label-schema.schema-version="1.0" \ 40 | org.label-schema.usage="https://github.com/overkill32/hassio-remote-backup/tree/master/README.md" \ 41 | org.label-schema.vcs-ref=${BUILD_REF} \ 42 | org.label-schema.vcs-url="https://github.com/overkill32/hassio-remote-backup/" \ 43 | org.label-schema.vendor="Hass.io add-ons by Nicolai" 44 | -------------------------------------------------------------------------------- /remote-backup/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Remote Backup", 3 | "version": "dev", 4 | "slug": "remote_backup", 5 | "description": "Exploit snapshots and SCP to create remote backups to specified server", 6 | "url": "https://github.com/overkill32/hassio-remote-backup", 7 | "startup": "once", 8 | "boot": "manual", 9 | "hassio_api": true, 10 | "hassio_role": "backup", 11 | "map": ["backup:rw"], 12 | "arch": ["armhf", "armv7", "aarch64", "amd64", "i386"], 13 | "options": { 14 | "ssh_host": "", 15 | "ssh_port": 22, 16 | "ssh_user": "", 17 | "ssh_key": [], 18 | "remote_directory": "", 19 | "zip_password": "", 20 | "keep_local_backup": "" 21 | }, 22 | "schema": { 23 | "ssh_host": "str", 24 | "ssh_port": "int", 25 | "ssh_user": "str", 26 | "ssh_key": ["str"], 27 | "remote_directory": "str", 28 | "zip_password": "str", 29 | "keep_local_backup": "match(^(all|[+]?\\d*)$)" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /remote-backup/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | CONFIG_PATH=/data/options.json 5 | 6 | # parse inputs from options 7 | SSH_HOST=$(jq --raw-output ".ssh_host" $CONFIG_PATH) 8 | SSH_PORT=$(jq --raw-output ".ssh_port" $CONFIG_PATH) 9 | SSH_USER=$(jq --raw-output ".ssh_user" $CONFIG_PATH) 10 | SSH_KEY=$(jq --raw-output ".ssh_key[]" $CONFIG_PATH) 11 | REMOTE_DIRECTORY=$(jq --raw-output ".remote_directory" $CONFIG_PATH) 12 | ZIP_PASSWORD=$(jq --raw-output '.zip_password' $CONFIG_PATH) 13 | KEEP_LOCAL_BACKUP=$(jq --raw-output '.keep_local_backup' $CONFIG_PATH) 14 | 15 | # create variables 16 | SSH_ID="${HOME}/.ssh/id" 17 | 18 | function add-ssh-key { 19 | echo "Adding SSH key" 20 | mkdir -p ~/.ssh 21 | ( 22 | echo "Host remote" 23 | echo " IdentityFile ${HOME}/.ssh/id" 24 | echo " HostName ${SSH_HOST}" 25 | echo " User ${SSH_USER}" 26 | echo " Port ${SSH_PORT}" 27 | echo " StrictHostKeyChecking no" 28 | ) > "${HOME}/.ssh/config" 29 | 30 | while read -r line; do 31 | echo "$line" >> ${HOME}/.ssh/id 32 | done <<< "$SSH_KEY" 33 | 34 | chmod 600 "${HOME}/.ssh/config" 35 | chmod 600 "${HOME}/.ssh/id" 36 | } 37 | 38 | function copy-backup-to-remote { 39 | 40 | cd /backup/ 41 | if [[ -z $ZIP_PASSWORD ]]; then 42 | echo "Copying ${slug}.tar to ${REMOTE_DIRECTORY} on ${SSH_HOST} using SCP" 43 | scp -F "${HOME}/.ssh/config" "${slug}.tar" remote:"${REMOTE_DIRECTORY}" 44 | else 45 | echo "Copying password-protected ${slug}.zip to ${REMOTE_DIRECTORY} on ${SSH_HOST} using SCP" 46 | zip -P "$ZIP_PASSWORD" "${slug}.zip" "${slug}".tar 47 | scp -F "${HOME}/.ssh/config" "${slug}.zip" remote:"${REMOTE_DIRECTORY}" && rm "${slug}.zip" 48 | fi 49 | 50 | } 51 | 52 | function delete-local-backup { 53 | 54 | hassio snapshots reload 55 | 56 | if [[ ${KEEP_LOCAL_BACKUP} == "all" ]]; then 57 | : 58 | elif [[ -z ${KEEP_LOCAL_BACKUP} ]]; then 59 | echo "Deleting local backup: ${slug}" 60 | hassio snapshots remove -name "${slug}" 61 | else 62 | 63 | last_date_to_keep=$(hassio snapshots list | jq .data.snapshots[].date | sort -r | \ 64 | head -n "${KEEP_LOCAL_BACKUP}" | tail -n 1 | xargs date -D "%Y-%m-%dT%T" +%s --date ) 65 | 66 | hassio snapshots list | jq -c .data.snapshots[] | while read backup; do 67 | if [[ $(echo ${backup} | jq .date | xargs date -D "%Y-%m-%dT%T" +%s --date ) -lt ${last_date_to_keep} ]]; then 68 | echo "Deleting local backup: $(echo ${backup} | jq -r .slug)" 69 | hassio snapshots remove -name "$(echo ${backup} | jq -r .slug)" 70 | fi 71 | done 72 | 73 | fi 74 | } 75 | 76 | function create-local-backup { 77 | name="Automated backup $(date +'%Y-%m-%d %H:%M')" 78 | echo "Creating local backup: \"${name}\"" 79 | slug=$(hassio snapshots new --options name="${name}" | jq --raw-output '.data.slug') 80 | echo "Backup created: ${slug}" 81 | } 82 | 83 | 84 | add-ssh-key 85 | create-local-backup 86 | copy-backup-to-remote 87 | delete-local-backup 88 | 89 | echo "Backup process done!" 90 | exit 0 91 | --------------------------------------------------------------------------------