digint / digint/btrbk

SSH filter script for raw targets on remote host

Open
#56 5 comments 0 reactions 0 assignees View on GitHub
enhancement
Dominant language
Perl
Stars
2.1k
Forks
139
PR merge metrics
No merged PRs in 30d

Description

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:-}; Remote: ${SSH_CLIENT:-})${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 ")
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
```

Contributor guide

No contributing guide indexed for this repository

Research direction

Start by reviewing the proposed SSH filter script in the issue, including its command filtering and restricted backup path. Determine how it should be integrated with btrbk for raw remote backups, then verify that allowed commands work and unsafe or out-of-path commands are rejected.

Written by the indexing model from the issue text.

Assessment

Tech stack
bash
Domain
cli, security
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.