digint / btrbk

Tool for creating snapshots and remote backups of btrfs subvolumes
https://digint.ch/btrbk/
GNU General Public License v3.0
1.66k stars 119 forks source link

SSH filter script for raw targets on remote host #56

Open smerschjohann opened 8 years ago

smerschjohann commented 8 years ago

I couldn't find a filtered ssh shell for backup on a remote server as raw files. I made a script for that:

#!/bin/bash

set -e
set -u

export PATH=/sbin:/bin:/usr/sbin:/usr/bin

enable_log=
use_sudo=
restrict_path_list=
allow_list=

log_cmd()
{
    if [[ -n "$enable_log" ]]; then
        logger -p $1 -t ssh_filter_btrbk.sh "$2 (Name: ${LOGNAME:-<unknown>}; Remote: ${SSH_CLIENT:-<unknown>})${3:+: $3}: $SSH_ORIGINAL_COMMAND"
    fi
}

allow_cmd()
{
    allow_list="${allow_list}|$1"
}

reject_and_die()
{
    local reason=$1
    log_cmd "auth.err" "btrbk REJECT" "$reason"
    echo "ERROR: ssh_filter_btrbk.sh: ssh command rejected: $reason: $SSH_ORIGINAL_COMMAND" 1>&2
    exit 1
}

run_cmd()
{
    log_cmd "auth.info" "btrbk ACCEPT"
    $use_sudo $SSH_ORIGINAL_COMMAND
}

reject_filtered_cmd()
{
    # note that the backslash is NOT a metacharacter in a POSIX bracket expression!
    option_match='-[a-zA-Z-]+'       # matches short as well as long options
    file_match='[0-9a-zA-Z_@+./-]+'  # matches file path (equal to $file_match in btrbk)

    if [[ -n "$restrict_path_list" ]]; then
    # match any of restrict_path_list with or without trailing slash,
    # or any file/directory (matching file_match) below restrict_path
    path_match="(${restrict_path_list})(/|/${file_match})?"
    else
    # match any absolute file/directory (matching file_match)
    path_match="/${file_match}"
    fi

    find_option_match='-[a-zA-Z-]+( [a-zA-Z0-9+]+)?'

    # allow multiple paths (e.g. "btrfs subvolume snapshot <src> <dst>")
    btrfs_cmd_match="^(${allow_list})( ${option_match})*( (of=)?$path_match)+( ${find_option_match})*$"

    if [[ ! $SSH_ORIGINAL_COMMAND =~ $btrfs_cmd_match ]] ; then
    reject_and_die "disallowed command${restrict_path_list:+ (restrict-path: \"${restrict_path_list//|/\", \"}\")}"
    fi
}

allow_cmd "test"
allow_cmd "dd status=none"
allow_cmd "find"

restrict_path_list=/home/btrbk/backups

# remove leading "|" on alternation lists
allow_list=${allow_list#\|}
restrict_path_list=${restrict_path_list#\|}

case "$SSH_ORIGINAL_COMMAND" in
    *\$*)     reject_and_die "unsafe character"     ;;
    *\&*)     reject_and_die "unsafe character"     ;;
    *\(*)     reject_and_die "unsafe character"     ;;
    *\{*)     reject_and_die "unsafe character"     ;;
    *\;*)     reject_and_die "unsafe character"     ;;
    *\<*)     reject_and_die "unsafe character"     ;;
    *\>*)     reject_and_die "unsafe character"     ;;
    *\`*)     reject_and_die "unsafe character"     ;;
    *\|*)     reject_and_die "unsafe character"     ;;
    *\.\./*)  reject_and_die "directory traversal"  ;;
    *)
    reject_filtered_cmd
    #echo $SSH_ORIGINAL_COMMAND
    run_cmd
    ;;
esac
digint commented 8 years ago

Thanks, I'll try to merge this into ssh_filter_btrbk.sh as soon as I find some time. This was not a high priority for me, as no root access is needed for raw targets (try setting ssh_user <myuser>), and I still consider it experimental (mainly because you don't get any error messages if the btrfs send stream is corrupt, which happened to me in the past).

calestyo commented 1 year ago

Is this still followed up?

Though I think, things would be much more restrictive... especially find (-delete) and dd (arbitrary if and or of) could be used to cause great harm.

calestyo commented 1 year ago

I start looking into to this (in addition with some more general overhaul) myself.... won't promise anything... but if someone should start work on this soon... it would perhaps be worth to coordinate.

digint commented 1 year ago

Note that for raw targets, there is no requirement to ssh as root. This making the support in ssh_filter_btrbk much less important, as access control can be managed via correct user permissions.

calestyo commented 1 year ago

Yes, that's clear, but still, in practise user login is often similar to root due to security holes (take alone the "monthly" privilege escalation found in kernel user namespaces 😉)