Dockerize rdiff-backup to create a personal single-user instance with SSH and web interface provided by rdiffweb.
Project tree
These are contents of directories in a tree-like format.
$ tree --dirsfirst
. ├── rdiffweb-ssh │ └── Dockerfile ├── rdiffweb-ui │ ├── docker-entrypoint.sh │ ├── Dockerfile │ └── rdw.conf ├── repositories ├── docker-compose.yml └── hash_password.py
Docker compose file and two docker images at a glance.
SHA1 password hash
At first, you need to know how to generate a SHA1 password hash.
$ cat hash_password.py
#!/usr/bin/env python3 # -*- coding: utf-8 -*- # rdiffweb, A web interface to rdiff-backup repositories # Copyright (C) 2019 rdiffweb contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. ''' Created on Apr. 10, 2020 @author: patrik dufresne ''' from base64 import b64decode, b64encode from builtins import str import hashlib import os import sys def hash_password(password): assert password and isinstance(password, str) password = password.encode(encoding='utf8') salt = os.urandom(4) h = hashlib.sha1(password) h.update(salt) return "{SSHA}" + b64encode(h.digest() + salt).decode('latin1') number_of_parameters = len(sys.argv) for n in range(1, number_of_parameters): print("{:30s} {:s}".format(sys.argv[n], hash_password(sys.argv[n])))
Generate a SHA1 password hash.
$ python3 hash_password.py rdiff
rdiff {SSHA}TJrN3DS0sfu29weUUs+0Hoif+NG7V4K0
Docker for the SSH access
This is a simple Docker image that provides limited SSH access for rdiff-backup
user.
INITIAL_SHA1PASS
is set to rdiff
.$ cat rdiffweb-ssh/Dockerfile
FROM alpine:3.12 MAINTAINER Milosz Galazka <milosz@sleeplessbeastie.eu> ARG DOCKER_UID=5000 ARG DOCKER_GID=5000 RUN set -eux \ && apk add --no-cache shadow \ && addgroup -g $DOCKER_GID -S rdiff-backup \ && adduser -D -u $DOCKER_UID -h /rdiff-backup -s /bin/ash -G rdiff-backup -g rdiff-backup rdiff-backup \ && usermod -p '*' rdiff-backup RUN set -eux \ && apk add --no-cache rdiff-backup python3 RUN set -eux \ && apk add --no-cache openssh \ && ssh-keygen -f /etc/ssh/ssh_host_rsa_key -N '' -t rsa \ && date \ && echo "PasswordAuthentication no" | tee -a /etc/ssh/sshd_config \ && echo "AllowTcpForwarding No" | tee -a /etc/ssh/sshd_config \ && echo "AllowUsers rdiff-backup" | tee -a /etc/ssh/sshd_config \ && echo "Match User rdiff-backup" | tee -a /etc/ssh/sshd_config \ && echo " ForceCommand rdiff-backup --server" | tee -a /etc/ssh/sshd_config RUN set -eux \ && mkdir /rdiff-backup/.ssh \ && touch /rdiff-backup/.ssh/authorized_keys \ && chown rdiff-backup:rdiff-backup /rdiff-backup/.ssh /rdiff-backup/.ssh/authorized_keys \ && chmod 700 /rdiff-backup/.ssh \ && chmod 600 /rdiff-backup/.ssh/authorized_keys EXPOSE 22 ENTRYPOINT ["/usr/sbin/sshd", "-D"]
Docker for the web interface
This Docker image serves web interface and manages SSH keys.
$ cat rdiffweb-ui/Dockerfile
FROM alpine:3.12 MAINTAINER Milosz Galazka <milosz@sleeplessbeastie.eu> ARG DOCKER_UID=5000 ARG DOCKER_GID=5000 ENV INITIAL_SHA1PASS="{SSHA}TJrN3DS0sfu29weUUs+0Hoif+NG7V4K0" RUN set -eux \ && addgroup -g $DOCKER_GID -S rdiff-backup \ && adduser -D -u $DOCKER_UID -h /rdiff-backup -s /sbin/nologin -G rdiff-backup -g rdiff-backup rdiff-backup RUN set -eux \ && apk add --no-cache python3 py3-pip libldap rdiff-backup sqlite \ && apk add --no-cache --virtual .build_deps build-base linux-headers python3-dev py-setuptools openldap-dev \ && pip install rdiffweb \ && apk del .build_deps RUN set -eux \ && mkdir /rdiff-backup/.ssh \ && touch /rdiff-backup/.ssh/authorized_keys \ && chown rdiff-backup:rdiff-backup /rdiff-backup/.ssh /rdiff-backup/.ssh/authorized_keys \ && chmod 700 /rdiff-backup/.ssh \ && chmod 600 /rdiff-backup/.ssh/authorized_keys RUN set -eux \ && mkdir /etc/rdiffweb \ && chown rdiff-backup:rdiff-backup /etc/rdiffweb COPY rdw.conf /etc/rdiffweb/rdw.conf RUN set -eux \ && chown rdiff-backup:rdiff-backup /etc/rdiffweb/rdw.conf ADD docker-entrypoint.sh /usr/local/bin/ RUN chmod +x /usr/local/bin/docker-entrypoint.sh #VOLUME /etc/rdiff-backup EXPOSE 8080 USER rdiff-backup WORKDIR /rdiff-backup ENTRYPOINT ["docker-entrypoint.sh"] CMD ["rdiffweb"]
It contains the default rdiffweb configuration.
$ cat rdiffweb-ui/rdw.conf
# Host name or IP address to bind to. # If you want to bind to all interface, use 0.0.0.0 # Default: 127.0.0.1 ServerHost=0.0.0.0 # TCP port on which to listen. Default: 8080 ServerPort=8080 # Define logging level. (ERROR, WARNING, INFO, DEBUG) Default: INFO LogLevel=INFO # Define where log stuff. Default to console. #LogFile=/var/log/rdiffweb.log #LogAccessFile=/var/log/rdiffweb-access.log # Define the kind of environment where rdiffweb is running. Depending of this # configuration, behaviours related to exception handling are different. In # "development" environment, stacktrace might be shown. In "production" # environment those are hidden to user. Environment=production # Customizing rdiffweb #FavIcon=/etc/rdiffweb/favicon.ico #HeaderLogo=/etc/rdiffweb/logo.jpg #HeaderName=rdiffweb #WelcomeMsg= #WelcomeMsg[fr]= #WelcomeMsg[ru]= # Define the color scheme. Option: default, orange. #DefaultTheme=orange # Temporary location where rdiffweb should restore your data and create # archive if necessary to restore. This configuration parameter may be useful # when your /tmp folder is very small. #tempdir=/tmp # The time of day when notification emails are sent out. (Default: 23:00). #EmailNotificationTime=23:00 # The SMTP server name (Required). #EmailHost=smtp.server.com # Encryption to be use if Any. Option: ssl or starttls (Default: none). #EmailEncryption=none # Define the 'From:' (Required) #EmailSender=john@doe.com # May be blank, if the smtp server does not require authentication #EmailUsername=email_user # May be blank, if the smtp server does not require authentication #EmailPassword=email_password # Send confirmation mail if user changed his email address. #EmailSendChangedNotification=true # Update user repositories every 15 minutes # AutoUpdateRepos=15 # If the user/password are valid (found in LDAP) create the user # in the database. Default: false #AddMissingUser=false #----- Enable Sqlite DB Authentication. #SQLiteDBFile=/etc/rdiffweb/rdw.db #----- Enable LDAP Authentication # The uri parameter may be a comma- or whitespace-separated list of URIs # containing only the schema, the host, and the port fields. #LdapUri=ldap://localhost:389 # An aditional Ldap query filter to limit the search #LdapFilter=(objectClass=posixAccount) # This directive specifies an LDAP group whose members are allowed access. It # takes the distinguished name of the LDAP group. #LdapRequiredGroup=cn=Administrators,dc=nodomain # Set to true to enable TLS (optional, default:false) #LdapTls=true # The DN of the branch of the directory where all searches should start from. #LdapBaseDn=dc=nodomain # An optional DN used to bind to the server when searching for entries. If not # provided, will use an anonymous bind. #LdapBindDn=cn=admin,dc=nodomain # A bind password to use in conjunction with the bind DN. Note that the bind # password is probably sensitive data, and should be properly protected. You # should only use the LdapBindDn and LdapBindPassword if you absolutely # need them to search the directory. #LdapBindPassword=my_secret # Limit on waiting for a network response, in seconds. (default:10) #LdapNetworkTimeout=10 # Limit on waiting for any response, in seconds. #LdapTimeout=300 # Version of LDAP in use either 2 or 3. #LdapProtocolVersion=2 #LdapProtocolVersion=3 # When set to True, allow LDAP users to update their password using rdiffweb # web interface. Otherwise, LDAP users cannot update their password. #LdapAllowPasswordChange=true # Enable verification of ShadowExpire. #LdapCheckShadowExpire=true
There is also a shell script to perform initial user configuration. You can define INITIAL_SHA1PASS
, INITIAL_THEME
and INITIAL_HEADER
.
$ cat rdiffweb-ui/docker-entrypoint.sh
#!/bin/sh set -eux RDIFFWEB_DATABASE="/etc/rdiffweb/rdw.db" RDIFFWEB_CONFIG="/etc/rdiffweb/rdw.conf" RDIFFWEB_HOME="/rdiff-backup" # get password from file if [ -n "${INITIAL_SHA1PASS_FILE+x}" ]; then INITIAL_SHA1PASS="$(< "$INITIAL_SHA1PASS_FILE")" fi if [ "$1" = 'rdiffweb' ]; then # set username, password if [ -n "$INITIAL_SHA1PASS" ]; then if [ -f "$RDIFFWEB_DATABASE" ]; then CURRENT_COUNT="$(echo "select count(*) from users where userID=1" | sqlite3 $RDIFFWEB_DATABASE)" CURRENT_SHA1PASS="$(echo "select Password from users where userID=1" | sqlite3 $RDIFFWEB_DATABASE)" if [ "$CURRENT_COUNT" -eq "1" ]; then if [ "$CURRENT_SHA1PASS" != "$INITIAL_SHA1PASS" ]; then echo "update users set Password=\"$INITIAL_SHA1PASS\" where userID=1" | sqlite3 $RDIFFWEB_DATABASE fi else echo "INSERT INTO users VALUES(1,\"admin\",\"$INITIAL_SHA1PASS\",\"\",0,\"$RDIFFWEB_HOME\",1,0)" | sqlite3 $RDIFFWEB_DATABASE fi else cat <<-EOF | sqlite3 $RDIFFWEB_DATABASE PRAGMA foreign_keys=OFF; BEGIN TRANSACTION; CREATE TABLE users ( UserID integer primary key autoincrement, Username varchar (50) unique NOT NULL, Password varchar (40) NOT NULL DEFAULT "", UserRoot varchar (255) NOT NULL DEFAULT "", IsAdmin tinyint NOT NULL DEFAULT FALSE, UserEmail varchar (255) NOT NULL DEFAULT "", RestoreFormat tinyint NOT NULL DEFAULT TRUE, role tinyint NOT NULL DEFAULT "10"); INSERT INTO users VALUES(1,"admin","$INITIAL_SHA1PASS","",0,"$RDIFFWEB_HOME",1,0); CREATE TABLE repos ( RepoID integer primary key autoincrement, UserID int(11) NOT NULL, RepoPath varchar (255) NOT NULL, MaxAge tinyint NOT NULL DEFAULT 0, Encoding varchar (50), keepdays varchar(255) NOT NULL DEFAULT ""); CREATE TABLE sshkeys ( Fingerprint primary key, Key clob UNIQUE, UserID int(11) NOT NULL); DELETE FROM sqlite_sequence; INSERT INTO sqlite_sequence VALUES('users',2); INSERT INTO sqlite_sequence VALUES('repos',1); COMMIT; EOF fi fi # set home CURRENT_HOME="$(echo "select UserRoot from users where userID=1" | sqlite3 $RDIFFWEB_DATABASE)" if [ "$CURRENT_HOME" != "$RDIFFWEB_HOME" ]; then echo "update users set UserRoot=\"$RDIFFWEB_HOME\" where userID=1" | sqlite3 $RDIFFWEB_DATABASE fi # set theme, header name if [ -n "$INITIAL_THEME" ]; then sed -i -e "s/^#\?DefaultTheme=.*/DefaultTheme=${INITIAL_THEME}/" $RDIFFWEB_CONFIG fi if [ -n "$INITIAL_HEADER" ]; then sed -i -e "s/^#\?HeaderName=.*/HeaderName=${INITIAL_HEADER}/" $RDIFFWEB_CONFIG fi exec /usr/bin/rdiffweb "$@" fi exec "$@"
Docker compose
Put everything together using Docker compose.
$ cat docker-compose.yml
version: "3.3" services: rdiffweb-ui: build: context: rdiffweb-ui args: DOCKER_UID: 1000 DOCKER_GID: 1000 environment: INITIAL_SHA1PASS: "{SSHA}ErnSUDB9Za33sI23om75Odl4KaH83oKU" INITIAL_HEADER: "Personal backup" INITIAL_THEME: "orange" ports: - 8080:8080 volumes: - ./repositories:/rdiff-backup/repositories - ssh_config:/rdiff-backup/.ssh - rdiffweb_config:/etc/rdiffweb rdiffweb-ssh: build: context: rdiffweb-ssh args: DOCKER_UID: 1000 DOCKER_GID: 1000 ports: - "2222:22" volumes: - ./repositories:/rdiff-backup/repositories - ssh_config:/rdiff-backup/.ssh volumes: ssh_config: rdiffweb_config:
UID:1000
/GID:1000
. INITIAL_SHA1PASS
is set to milosz
.Usage
SSH port 2222
, WWW port 8080
.
Define --remote-schema
parameter to use it locally.
$ rdiff-backup --print-statistics --remote-schema "ssh -C -p 2222 %s rdiff-backup --server" ~/Projects/ansible/ rdiff-backup@127.0.0.1::/rdiff-backup/repositories/ansible
--------------[ Session statistics ]-------------- StartTime 1599402464.00 (Sun Sep 6 14:27:44 2020) EndTime 1599402464.50 (Sun Sep 6 14:27:44 2020) ElapsedTime 0.50 (0.50 seconds) SourceFiles 12 SourceFileSize 649894 (635 KB) MirrorFiles 1 MirrorFileSize 0 (0 bytes) NewFiles 11 NewFileSize 649894 (635 KB) DeletedFiles 0 DeletedFileSize 0 (0 bytes) ChangedFiles 1 ChangedSourceSize 0 (0 bytes) ChangedMirrorSize 0 (0 bytes) IncrementFiles 0 IncrementFileSize 0 (0 bytes) TotalDestinationSizeChange 649894 (635 KB) Errors 0 --------------------------------------------------
Additional notes
Use HAProxy to install it at home on your mini-PC.
The source code is available at GitLab.
There is also Minarca, which is a simple data backup application, that’s enough for today.