From d7723be158f995c2a43527720baba9d4a2687fd9 Mon Sep 17 00:00:00 2001 From: Valentin Lab Date: Wed, 6 May 2020 16:43:28 +0200 Subject: [PATCH] new: [rsync-backup] add charm Signed-off-by: Valentin Lab --- .../build/src/etc/ssh/sshd_config | 4 +- rsync-backup/build/Dockerfile | 27 +++++++ rsync-backup/build/entrypoint.sh | 71 +++++++++++++++++++ rsync-backup/hooks/backup-relation-joined | 35 +++++++++ rsync-backup/hooks/init | 22 ++++++ .../hooks/schedule_command-relation-joined | 48 +++++++++++++ rsync-backup/lib/common | 5 ++ rsync-backup/metadata.yml | 17 +++++ 8 files changed, 227 insertions(+), 2 deletions(-) create mode 100644 rsync-backup/build/Dockerfile create mode 100755 rsync-backup/build/entrypoint.sh create mode 100755 rsync-backup/hooks/backup-relation-joined create mode 100755 rsync-backup/hooks/init create mode 100755 rsync-backup/hooks/schedule_command-relation-joined create mode 100644 rsync-backup/lib/common create mode 100644 rsync-backup/metadata.yml diff --git a/rsync-backup-target/build/src/etc/ssh/sshd_config b/rsync-backup-target/build/src/etc/ssh/sshd_config index 6cfe4a7..fcaee64 100644 --- a/rsync-backup-target/build/src/etc/ssh/sshd_config +++ b/rsync-backup-target/build/src/etc/ssh/sshd_config @@ -29,7 +29,7 @@ # Authentication: #LoginGraceTime 2m -#PermitRootLogin prohibit-password +PermitRootLogin no #StrictModes yes #MaxAuthTries 6 #MaxSessions 10 @@ -96,7 +96,7 @@ X11Forwarding no #Compression delayed #ClientAliveInterval 0 #ClientAliveCountMax 3 -#UseDNS no +UseDNS no #PidFile /run/sshd.pid #MaxStartups 10:30:100 PermitTunnel no diff --git a/rsync-backup/build/Dockerfile b/rsync-backup/build/Dockerfile new file mode 100644 index 0000000..19911ff --- /dev/null +++ b/rsync-backup/build/Dockerfile @@ -0,0 +1,27 @@ +FROM alpine:3.9 + +MAINTAINER Valentin Lab + + +RUN apk add bash rsync sudo openssh-client + +# RUN apt-get update && \ +# DEBIAN_FRONTEND=noninteractive apt-get install --force-yes -y --no-install-recommends rsync sudo openssh-client && \ +# apt-get clean && \ +# rm -rf /var/lib/apt/lists/* + +## New user/group rsync/rsync with home dir in /var/lib/rsync +RUN mkdir -p /var/lib/rsync && \ + addgroup -S rsync && \ + adduser -S rsync -h /var/lib/rsync -G rsync && \ + chown rsync:rsync /var/lib/rsync + +## New user/group rsync/rsync with home dir in /var/lib/rsync +# RUN mkdir -p /var/lib/rsync && \ +# groupadd -r rsync && \ +# useradd -r rsync -d /var/lib/rsync -g rsync && \ +# chown rsync:rsync /var/lib/rsync + +COPY ./entrypoint.sh /entrypoint.sh + +ENTRYPOINT [ "/entrypoint.sh" ] diff --git a/rsync-backup/build/entrypoint.sh b/rsync-backup/build/entrypoint.sh new file mode 100755 index 0000000..d801829 --- /dev/null +++ b/rsync-backup/build/entrypoint.sh @@ -0,0 +1,71 @@ +#!/bin/bash + +[ "$UID" != "0" ] && echo "You must be root." 2>&1 && exit 1 + +## +## code +## + + +user=rsync +src="$1" + +if [ -z "$src" ]; then + echo "You must provide a source directory as first argument." >&2 + exit 1 +fi + +if ! [ -d "$src" ]; then + echo "Folder '$src' not found, please provide a valid directory as first argument." >&2 + exit 1 +fi + +dest="$2" + +if [ -z "$dest" ]; then + echo "You must provide a host as target." >&2 + exit 1 +fi + + +rsync_options=(${RSYNC_OPTIONS:-}) +ssh_options=(${SSH_OPTIONS:--o StrictHostKeyChecking=no}) + + +if echo "$dest" | grep "/" >/dev/null 2>&1; then + rsync_options=("--bwlimit" "$(echo "$dest" | cut -f 2 -d "/")" "${rsync_options[@]}") + dest="$(echo "$dest" | cut -f 1 -d "/")" +fi + +if echo "$dest" | grep ":" >/dev/null 2>&1; then + ssh_options=("-p" "$(echo "$dest" | cut -f 2 -d ":")" "${ssh_options[@]}") + dest="$(echo "$dest" | cut -f 1 -d ":")" +fi + + +hostname=$(hostname) +hostname=${LABEL_HOSTNAME:-$hostname} + +dest_path="/var/mirror/$hostname" + +touch /etc/rsync/exclude-patterns +touch /etc/rsync/include-patterns + +if ! [ -s "/etc/rsync/include-patterns" ]; then + echo "Nothing to do as /etc/rsync/include-patterns is empty." + exit 0 +fi + +cmd=(/usr/bin/rsync "${rsync_options[@]}" -azvA + -e "sudo -u $user ssh ${ssh_options[*]}" + --include-from /etc/rsync/include-patterns + --exclude-from /etc/rsync/exclude-patterns + --delete --partial --partial-dir .rsync-partial + --numeric-ids "$src/" "$user@$dest":"$dest_path") + +rsync_uid_gid=$(stat -c "%u:%g" "/var/lib/rsync") +chown "$rsync_uid_gid" "/var/lib/rsync/.ssh" -R + +echo "${cmd[@]}" + +exec "${cmd[@]}" \ No newline at end of file diff --git a/rsync-backup/hooks/backup-relation-joined b/rsync-backup/hooks/backup-relation-joined new file mode 100755 index 0000000..f0132dd --- /dev/null +++ b/rsync-backup/hooks/backup-relation-joined @@ -0,0 +1,35 @@ +#!/bin/bash + +## When writing relation script, remember: +## - they should be idempotents +## - they can be launched while the dockers is already up +## - they are launched from the host +## - the target of the link is launched first, and get a chance to ``relation-set`` +## - both side of the scripts get to use ``relation-get``. + +. lib/common + +set -e + +exclude_patterns=$(relation-get "exclude-patterns") || true +include_patterns=$(relation-get "include-patterns") || true +include_patterns=${include_patterns:-"- /"} + +while read-0 pattern; do + if [[ "$pattern" != /* ]]; then + err "Invalid pattern '$pattern' provided as ${WHITE}exclude-pattern${NORMAL}, " \ + "in relation's options." + exit 1 + fi + echo "/$BASE_SERVICE_NAME$pattern" +done < <(e "$exclude_patterns" | shyaml get-values-0) >> "$RSYNC_EXCLUDE_PATTERNS" + +while read-0 pattern; do + if [[ "$pattern" != /* ]]; then + err "Invalid pattern '$pattern' provided as ${WHITE}exclude-pattern${NORMAL}, " \ + "in relation's options." + exit 1 + fi + echo "/$BASE_SERVICE_NAME$pattern" +done < <(e "$include_patterns" | shyaml get-values-0) >> "$RSYNC_INCLUDE_PATTERNS" + diff --git a/rsync-backup/hooks/init b/rsync-backup/hooks/init new file mode 100755 index 0000000..c089112 --- /dev/null +++ b/rsync-backup/hooks/init @@ -0,0 +1,22 @@ +#!/bin/bash + +## Init is run on host +## For now it is run every time the script is launched, but +## it should be launched only once after build. + +## Accessible variables are: +## - SERVICE_NAME Name of current service +## - DOCKER_BASE_IMAGE Base image from which this service might be built if any +## - SERVICE_DATASTORE Location on host of the DATASTORE of this service +## - SERVICE_CONFIGSTORE Location on host of the CONFIGSTORE of this service + +. lib/common + +set -e + + +RSYNC_EXCLUDE_PATTERNS="$SERVICE_CONFIGSTORE/etc/rsync/exclude-patterns" + +mkdir -p "${RSYNC_EXCLUDE_PATTERNS%/*}" +echo "/*/" > "$RSYNC_EXCLUDE_PATTERNS" ## start a new +echo > "$RSYNC_INCLUDE_PATTERNS" ## start a new diff --git a/rsync-backup/hooks/schedule_command-relation-joined b/rsync-backup/hooks/schedule_command-relation-joined new file mode 100755 index 0000000..0b8c175 --- /dev/null +++ b/rsync-backup/hooks/schedule_command-relation-joined @@ -0,0 +1,48 @@ +#!/bin/bash + +## When writing relation script, remember: +## - they should be idempotents +## - they can be launched while the dockers is already up +## - they are launched from the host +## - the target of the link is launched first, and get a chance to ``relation-set`` +## - both side of the scripts get to use ``relation-get``. + +. lib/common + +set -e + +schedule=$(relation-get schedule) + +if ! echo "$schedule" | egrep '^\s*(([0-9/,*-]+\s+){4,4}[0-9/,*-]+|@[a-z]+)\s*$' >/dev/null 2>&1; then + err "Unrecognized schedule '$schedule'." + exit 1 +fi + +private_key=$(options-get private-key) || exit 1 +target=$(options-get target) || exit 1 +ident=$(options-get ident) || exit 1 + +home=/var/lib/rsync +local_path_key=$home/.ssh +host_path_key="$SERVICE_CONFIGSTORE${local_path_key}" + +echo "$private_key" | file_put "$host_path_key/id_rsa" +chmod 600 "$host_path_key/id_rsa" + +label="${SERVICE_NAME}" +DST=$CONFIGSTORE/$TARGET_CHARM_NAME/etc/cron/$label + +## Warning: using '\' in heredoc will be removed in the final cron file, which +## is totally wanted: cron does not support multilines. +file_put "$DST" <&1 | ts '\%F \%T' >> /var/log/cron/${label}_script.log +EOF +chmod +x "$DST" diff --git a/rsync-backup/lib/common b/rsync-backup/lib/common new file mode 100644 index 0000000..4f87ee8 --- /dev/null +++ b/rsync-backup/lib/common @@ -0,0 +1,5 @@ + + +RSYNC_CONFIG_DIR="$SERVICE_CONFIGSTORE/etc/rsync" +RSYNC_INCLUDE_PATTERNS="$RSYNC_CONFIG_DIR/include-patterns" +RSYNC_EXCLUDE_PATTERNS="$RSYNC_CONFIG_DIR/exclude-patterns" diff --git a/rsync-backup/metadata.yml b/rsync-backup/metadata.yml new file mode 100644 index 0000000..4afd39e --- /dev/null +++ b/rsync-backup/metadata.yml @@ -0,0 +1,17 @@ +description: Rsync Backup Service +type: run-once + +provides: + backup: + +uses: + schedule-command: + constraint: required + auto: summon + solves: + missing-feature: "scheduling of backups" + default-options: + ## backup every day on random time + schedule: !bash-stdout | + printf "%d %d * * *" "$((RANDOM % 60))" "$((RANDOM % 6))" +