mirror of https://github.com/digint/btrbk
ssh_filter_btrbk: refactoring/hardening:
- switched to bash interpreter - enable fine-grained (--source, --target, ...) capabilities by command-line options - added "--restrict_path" command-line option - added sudo flag - added man-page - print SSH_ORIGINAL_COMMAND in error messagepull/48/head
parent
28abe96747
commit
f01304df35
|
@ -0,0 +1,97 @@
|
||||||
|
.TH "ssh_filter_btrbk" "1" "2015-09-03" "btrbk v0.20.0" ""
|
||||||
|
.\" disable hyphenation
|
||||||
|
.nh
|
||||||
|
.\" disable justification (adjust text to left margin only)
|
||||||
|
.ad l
|
||||||
|
.SH NAME
|
||||||
|
ssh_filter_btrbk.sh \- ssh command filter script for btrbk
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.nf
|
||||||
|
\fBssh_filter_btrbk.sh\fR [\-s|\-\-source] [\-t|\-\-target] [\-d|\-\-delete] [\-i|\-\-info]
|
||||||
|
[\-p|\-\-restrict\-path <path>] [\-l|\-\-log] [\-\-sudo]
|
||||||
|
.fi
|
||||||
|
.SH DESCRIPTION
|
||||||
|
\fBssh_filter_btrbk.sh\fR restricts SSH commands to \fIbtrfs\fR
|
||||||
|
commands used by \fIbtrbk\fR. It examines the SSH_ORIGINAL_COMMAND
|
||||||
|
environment variable (set by sshd) and executes it only if it matches
|
||||||
|
commands used by \fIbtrbk\fR. The accepted commands are specified by
|
||||||
|
the "\-\-source", "\-\-target", "\-\-delete" and "\-\-info" options.
|
||||||
|
.PP
|
||||||
|
Note that the following btrfs commands are always allowed: "btrfs
|
||||||
|
subvolume show", "btrfs subvolume list".
|
||||||
|
.PP
|
||||||
|
Example line in /root/.ssh/authorized_keys on a backup target host:
|
||||||
|
.PP
|
||||||
|
.RS 4
|
||||||
|
.nf
|
||||||
|
command="ssh_filter_btrbk.sh \-\-target \-\-delete \-\-restrict\-path /mnt/btr_backup" ssh\-rsa AAAAB3NzaC1...hwumXFRQBL btrbk@mydomain.com
|
||||||
|
.fi
|
||||||
|
.RE
|
||||||
|
.SH OPTIONS
|
||||||
|
.PP
|
||||||
|
\-s, \-\-source
|
||||||
|
.RS 4
|
||||||
|
Allow commands for backup source: "btrfs subvolume snapshot", "btrfs
|
||||||
|
send". Equivalent to "\-\-snapshot \-\-send".
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
\-t, \-\-target
|
||||||
|
.RS 4
|
||||||
|
Allow commands for backup target: "btrfs receive". Equivalent to
|
||||||
|
"\-\-receive".
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
\-d, \-\-delete
|
||||||
|
.RS 4
|
||||||
|
Allow commands for subvolume deletion: "btrfs subvolume delete". This
|
||||||
|
is used for backup source if \fIsnapshot_preserve_daily\fR is not set
|
||||||
|
to \[lq]all\[rq], and for backup targets if
|
||||||
|
\fItarget_preserve_daily\fR is not set to \[lq]all\[rq].
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
\-i, \-\-info
|
||||||
|
.RS 4
|
||||||
|
Allow informative commands: "btrfs subvolume find-new", "btrfs
|
||||||
|
filesystem usage". This is used by btrbk \fIinfo\fR and \fIdiff\fR
|
||||||
|
commands.
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
\-\-snapshot
|
||||||
|
.RS 4
|
||||||
|
Allow btrfs snapshot command: "btrfs subvolume snapshot".
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
\-\-send
|
||||||
|
.RS 4
|
||||||
|
Allow btrfs send command: "btrfs send".
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
\-\-receive
|
||||||
|
.RS 4
|
||||||
|
Allow btrfs receive command: "btrfs receive".
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
\-p, \-\-restrict\-path <path>
|
||||||
|
.RS 4
|
||||||
|
Restrict btrfs commands to <path>.
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
\-l, \-\-log
|
||||||
|
.RS 4
|
||||||
|
Log ACCEPT and REJECT messages to the system log.
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
\-\-sudo
|
||||||
|
.RS 4
|
||||||
|
Call SSH_ORIGINAL_COMMAND using sudo.
|
||||||
|
.RE
|
||||||
|
.SH AVAILABILITY
|
||||||
|
Please refer to the btrbk project page
|
||||||
|
\fBhttp://www.digint.ch/btrbk/\fR for further
|
||||||
|
details.
|
||||||
|
.SH SEE ALSO
|
||||||
|
.BR btrbk (1),
|
||||||
|
.BR btrbk.conf (5),
|
||||||
|
.BR btrfs (1)
|
||||||
|
.SH AUTHOR
|
||||||
|
Axel Burri <axel@tty0.ch>
|
|
@ -1,4 +1,4 @@
|
||||||
#!/bin/sh
|
#!/bin/bash
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
set -u
|
set -u
|
||||||
|
@ -6,47 +6,137 @@ set -u
|
||||||
export PATH=/sbin:/bin:/usr/sbin:/usr/bin
|
export PATH=/sbin:/bin:/usr/sbin:/usr/bin
|
||||||
|
|
||||||
enable_log=
|
enable_log=
|
||||||
if [ "$#" -ge 1 ] && [ "$1" = "-l" ]; then
|
use_sudo=
|
||||||
enable_log=1
|
restrict_path_list=
|
||||||
fi
|
allow_list=
|
||||||
|
|
||||||
log_cmd()
|
log_cmd()
|
||||||
{
|
{
|
||||||
if [ -n "$enable_log" ]; then
|
if [[ -n "$enable_log" ]]; then
|
||||||
logger -p $1 -t ssh_filter_btrbk.sh "$2 (Name: ${LOGNAME:-<unknown>}; Remote: ${SSH_CLIENT:-<unknown>}): $SSH_ORIGINAL_COMMAND"
|
logger -p $1 -t ssh_filter_btrbk.sh "$2 (Name: ${LOGNAME:-<unknown>}; Remote: ${SSH_CLIENT:-<unknown>})${3:+: $3}: $SSH_ORIGINAL_COMMAND"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
allow_cmd()
|
||||||
|
{
|
||||||
|
allow_list="${allow_list}|$1"
|
||||||
|
}
|
||||||
|
|
||||||
reject_and_die()
|
reject_and_die()
|
||||||
{
|
{
|
||||||
log_cmd "auth.err" "btrbk REJECT"
|
local reason=$1
|
||||||
/bin/echo "ERROR: ssh command rejected" 1>&2
|
log_cmd "auth.err" "btrbk REJECT" "$reason"
|
||||||
|
echo "ERROR: ssh_filter_btrbk.sh: ssh command rejected: $reason: $SSH_ORIGINAL_COMMAND" 1>&2
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
run_cmd()
|
run_cmd()
|
||||||
{
|
{
|
||||||
log_cmd "auth.info" "btrbk ACCEPT"
|
log_cmd "auth.info" "btrbk ACCEPT"
|
||||||
$SSH_ORIGINAL_COMMAND
|
$use_sudo $SSH_ORIGINAL_COMMAND
|
||||||
}
|
}
|
||||||
|
|
||||||
case "$SSH_ORIGINAL_COMMAND" in
|
reject_filtered_cmd()
|
||||||
*\$*) reject_and_die ;;
|
{
|
||||||
*\&*) reject_and_die ;;
|
# note that the backslash is NOT a metacharacter in a POSIX bracket expression!
|
||||||
*\(*) reject_and_die ;;
|
option_match='-[a-zA-Z-]+' # matches short as well as long options
|
||||||
*\{*) reject_and_die ;;
|
file_match='[0-9a-zA-Z_@+./-]+' # matches file path (equal to $file_match in btrbk)
|
||||||
*\;*) reject_and_die ;;
|
|
||||||
*\<*) reject_and_die ;;
|
if [[ -n "$restrict_path_list" ]]; then
|
||||||
*\>*) reject_and_die ;;
|
# match any of restrict_path_list with or without trailing slash,
|
||||||
*\`*) reject_and_die ;;
|
# or any file/directory (matching file_match) below restrict_path
|
||||||
*\|*) reject_and_die ;;
|
path_match="(${restrict_path_list})(/|/${file_match})?"
|
||||||
btrfs\ subvolume\ show\ *) run_cmd ;; # mandatory
|
else
|
||||||
btrfs\ subvolume\ list\ *) run_cmd ;; # mandatory
|
# match any absolute file/directory (matching file_match)
|
||||||
btrfs\ subvolume\ snapshot\ *) run_cmd ;; # mandatory if this host is backup source
|
path_match="/${file_match}"
|
||||||
btrfs\ send\ *) run_cmd ;; # mandatory if this host is backup source
|
fi
|
||||||
btrfs\ receive\ *) run_cmd ;; # mandatory if this host is backup target
|
|
||||||
btrfs\ subvolume\ delete\ *) run_cmd ;; # mandatory if scheduling is active
|
# allow multiple paths (e.g. "btrfs subvolume snapshot <src> <dst>")
|
||||||
btrfs\ subvolume\ find-new\ *) run_cmd ;; # needed for "btrbk diff"
|
btrfs_cmd_match="^(${allow_list})( ${option_match})*( $path_match)+$"
|
||||||
btrfs\ filesystem\ usage\ *) run_cmd ;; # needed for "btrbk info"
|
|
||||||
*) reject_and_die ;;
|
if [[ ! $SSH_ORIGINAL_COMMAND =~ $btrfs_cmd_match ]] ; then
|
||||||
|
reject_and_die "disallowed command${restrict_path_list:+ (restrict-path: \"${restrict_path_list//|/\", \"}\")}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
allow_cmd "btrfs subvolume show"; # subvolume queries are always allowed
|
||||||
|
allow_cmd "btrfs subvolume list"; # subvolume queries are always allowed
|
||||||
|
|
||||||
|
while [[ "$#" -ge 1 ]]; do
|
||||||
|
key="$1"
|
||||||
|
|
||||||
|
case $key in
|
||||||
|
-l|--log)
|
||||||
|
enable_log=1
|
||||||
|
;;
|
||||||
|
|
||||||
|
--sudo)
|
||||||
|
use_sudo="sudo"
|
||||||
|
;;
|
||||||
|
|
||||||
|
-p|--restrict-path)
|
||||||
|
restrict_path_list="${restrict_path_list}|${2%/}" # add to list while removing trailing slash
|
||||||
|
shift # past argument
|
||||||
|
;;
|
||||||
|
|
||||||
|
-s|--source)
|
||||||
|
allow_cmd "btrfs subvolume snapshot"
|
||||||
|
allow_cmd "btrfs send"
|
||||||
|
;;
|
||||||
|
|
||||||
|
-t|--target)
|
||||||
|
allow_cmd "btrfs receive"
|
||||||
|
;;
|
||||||
|
|
||||||
|
-d|--delete)
|
||||||
|
allow_cmd "btrfs subvolume delete"
|
||||||
|
;;
|
||||||
|
|
||||||
|
-i|--info)
|
||||||
|
allow_cmd "btrfs subvolume find-new"
|
||||||
|
allow_cmd "btrfs filesystem usage"
|
||||||
|
;;
|
||||||
|
|
||||||
|
--snapshot)
|
||||||
|
allow_cmd "btrfs subvolume snapshot"
|
||||||
|
;;
|
||||||
|
|
||||||
|
--send)
|
||||||
|
allow_cmd "btrfs send"
|
||||||
|
;;
|
||||||
|
|
||||||
|
--receive)
|
||||||
|
allow_cmd "btrfs receive"
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
echo "ERROR: ssh_filter_btrbk.sh: failed to parse command line option: $key" 1>&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
# 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
|
||||||
|
run_cmd
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
Loading…
Reference in New Issue