]> rtime.felk.cvut.cz Git - novaboot.git/blobdiff - server/novaboot-shell
server: Ensure that configuration can depend on logged-in user name
[novaboot.git] / server / novaboot-shell
index 0c476106ed6767fe9244081542d4acf89cf2aa45..52c70e47b95fe71e96fdbf68f677cfd981ee4275 100755 (executable)
@@ -10,11 +10,12 @@ die() {
 print_help() {
     cat <<EOF
 Target commands:
-- console
+- console (default command)
 - reset
 - on
 - off
 - rsync ...
+- get-config
 
 Management commands:
 - help
@@ -23,6 +24,7 @@ EOF
     if [ "$NB_ADMIN" ]; then
        cat <<EOF
 - add-key
+- shell (use with ssh -t)
 EOF
     fi
     exit 0
@@ -30,9 +32,31 @@ EOF
 
 add_key() {
     local user
+    [ "$NB_ADMIN" ] || return 1
 
+    case $# in
+       0) die "Usage: ssh ... add-key USERNAME < id_rsa.pub";;
+       1) break;;
+       *) die "User name must not contain spaces: $*";;
+    esac
     user="$1"
-    die "Not implemented"
+    key=$(cat)
+
+    tmp=$(mktemp ~/.ssh/authorized_keys.XXXXXXXX)
+    {
+       cat ~/.ssh/authorized_keys
+       echo "command=\"user $user\" $key"
+    } | sort -u > $tmp
+
+    mv $tmp ~/.ssh/authorized_keys
+}
+
+exec_shell() {
+    [ "$NB_ADMIN" ] || die "Permission denied"
+    if ! tty > /dev/null; then
+       echo "novaboot-shell: Consider starting the shell with 'ssh -t'"
+    fi
+    exec /bin/bash || exec /bin/sh
 }
 
 lock_queue() {
@@ -61,25 +85,22 @@ unlocked() {
     exec "$@"
 }
 
-check_var() {
-    if eval [ "\"\$$1\"" ]; then
-       return 0
-    else
-       die "$1 variable not defined in $CFG"
-    fi
+read_config() {
+    . "${NOVABOOT_SHELL_CONFIG:-$HOME/.novaboot-shell}"
 }
 
 # run_subcommand should be called only after permission checks and/or locking
 run_subcommand() {
+    read_config
     case "$*" in
        "console")
            trap "rm -f $RUN_DIR/ppid" EXIT
            echo $NOVABOOT_PPID > $RUN_DIR/ppid
            echo 'novaboot-shell: Connected'
-           # TODO: $target_reset_begin
-           check_var target_console && eval exec $target_console;;
+           # TODO: $reset_begin_cmd
+           eval exec "${console_cmd:?}";;
        "reset")
-           check_var target_reset && eval exec $target_reset;;
+           eval exec "${reset_cmd:?}";;
        "rsync --server "*" . .")
            if ! [ $# -eq 5 -o \( $# -eq 6 -a "$4" = '--log-format=X' \) ]; then
                die "Unexpected rsync invocation: $*"
@@ -88,29 +109,33 @@ run_subcommand() {
            cd "$HOME/tftproot"
            exec "$@";;
        "on")
-           check_var target_on && eval exec $target_on;;
+           eval exec "${on_cmd:?}";;
        "off")
-           check_var target_off && eval exec $target_off;;
+           eval exec "${off_cmd:?}";;
     esac
 }
 
 main() {
-    if [ "$1" = "-c" ]; then shift
-    else die "Permission denied"; fi
+    if [ "$1" = "-c" ]; then
+       set -- $2
+    elif [ $# -gt 0 ]; then
+       die "Permission denied"
+    fi
 
     NB_ADMIN=
-    if [ "${1%% *}" = "user" ]; then
+    if [ "$1" = "user" ]; then
        # Get user name encoded in ~/.ssh/authorized_keys
-       set -- $1
-       NB_USER="$2";
+       export NB_USER="$2";
        [ "$3" = "admin" ] && NB_ADMIN=1
        set -- $SSH_ORIGINAL_COMMAND
     fi
 
-    if [ $# -eq 0 ]; then print_help; fi
-
     IP=${SSH_CONNECTION%% *}
-    HOST=$(getent hosts $IP) || HOST=$IP
+    if [ "$IP" ]; then
+       HOST=$(getent hosts $IP) || HOST=$IP
+    else
+       HOST=localhost
+    fi
     REMOTE=${HOST##* }
     DATE=$(LANG=C date +'%F_%T')
     export NOVABOOT_ID="${NB_USER:-?} $DATE ${REMOTE}"
@@ -120,15 +145,16 @@ main() {
 
     case "$1" in
        # Commands allowed at any time
-       "console") locked $0 console;;
-       "get-config") target_config; exit;;
+       "console"|"") locked $0 console;;
+       "get-config") read_config && echo -n "${target_config}"; exit;;
        "add-key") shift; add_key "$@"; exit;;
+       "shell") exec_shell; exit;;
        "help") print_help;;
 
        # Commands allowed only when nobody or the same user is connected
        # to the console. "The same user" means that we were executed by
        # the same sshd process that has the lock. This is ensured by
-       # using SSH connection sharing on cline side.
+       # using SSH connection sharing on client side.
        reset | rsync | on | off)
            ALLOWED_PPID=$(cat $RUN_DIR/ppid 2>/dev/null || :)
            if [ "$PPID" -eq "${ALLOWED_PPID:-0}" ]; then run=unlocked; else run=locked; fi
@@ -140,18 +166,172 @@ main() {
     esac
 }
 
-RUN_DIR="$XDG_RUNTIME_DIR/novaboot"
-
-if [ "$NOVABOOT_SHELL_CONFIG" ]; then
-    CFG="$NOVABOOT_SHELL_CONFIG"
-else
-    CFG="$HOME/.novaboot-shell"
-fi
-
-. "$CFG"
+RUN_DIR="$HOME"
 
 if [ -z "$NOVABOOT_ID" ]; then
     main "$@"
 else
     run_subcommand "$@"
 fi
+exit
+
+: <<EOF
+=encoding utf8
+
+=head1 NAME
+
+novaboot-shell - provides novaboot with unified SSH-based interface for controlling target hardware
+
+=head1 SYNOPSIS
+
+B<novaboot-shell> -c "[command [arguments...]]"
+
+B<novaboot-shell> [command [arguments...]]
+
+B<ssh target@server> [command [arguments...]]
+
+=head1 DESCRIPTION
+
+B<novaboot-shell> provides L<novaboot(1)> with a unified SSH-based
+interface for controlling the target hardware. This simplifies
+client-side configuration, because clients typically need only the
+I<--ssh=...> option. B<novaboot-shell> is typically configured as a
+login shell of special user accounts associated with the target
+hardware (as set by L<adduser-novaboot(8)>). It ensures that users can
+perform only a limited set of actions (see L</COMMANDS> below) with
+the target and have no shell access on the server.
+
+=head1 COMMANDS
+
+=over 8
+
+=item console
+
+Connect to target console (usually serial line). When somebody is
+connected to the console, other users are blocked from controlling the
+target. Blocked users see a message indicating who blocks them.
+
+The user connected to the console is able to invoke other commands
+such as L</reset>, but only when the command is invoked via the same
+SSH connection. This can be accomplished by using SSH connection
+sharing, which is what L<novaboot(1)> uses (see I<-M> and I<-S> in
+L<ssh(1)>).
+
+This is the default command when no command is specified on command
+line.
+
+=item reset
+
+Reset the target hardware.
+
+=item on
+
+Power on the target hardware.
+
+=item off
+
+Power off the target hardware.
+
+=item rsync [...]
+
+This command is not meant to be invoked directly by the user. It
+allows using L<rsync(1)> to copy files to the target, perhaps for TFTP
+server. The rsync command must be invoked as: C<rsync ...
+target@server:>, i.e. without specifying destination path. The files
+will be stored into I<$HOME/tftproot>.
+
+=item user <uernamename> [admin]
+
+User command is meant to be used with C<command=> option in SSH's
+L<authorized_keys(5)> file. It allows the shell to display
+human-readable names when printing information about who blocks the
+target. Then, the real command is taken from SSH_ORIGINAL_COMMAND
+environment variable.
+
+When "admin" is specified after the user name, this user is considered
+an administrator and is allowed to run L</add-key> and L</shell>
+commands.
+
+=item get-config
+
+Prints novaboot configuration options needed for the target. One
+option per line.
+
+=back
+
+=head2 Administration commands
+
+Only administrators (see L</user>) are allowed to execute these
+commands.
+
+=over 8
+
+=item add-key <username>
+
+Reads the SSH public key from standard input and adds it into in
+F<~/.ssh/authorized_keys>.
+
+Example: C<ssh target@server add-key johndoe < john_rsa.pub>
+
+=item shell
+
+Runs shell on the server. Useful for editing configuration file. It is
+better used with allocated pseudo-terminal.
+
+Example: C<ssh -t target@server shell>
+
+=back
+
+=head1 CONFIGURATION FILE
+
+B<novaboot-shell> reads configuration file from
+F<$HOME/.novaboot-shell>. It should define values for the following
+variables in the SH syntax.
+
+=over 8
+
+=item console_cmd
+
+Command to C<exec> that connects to target's console.
+
+=item reset_cmd
+
+Command to C<exec> that resets the Target.
+
+=item on_cmd
+
+Command to C<exec> that powers the target on.
+
+=item off_cmd
+
+Command to C<exec> that powers the target off.
+
+=item target_config
+
+Novaboot command line options that specify which boot loader is used
+by the target (L<novaboot(1)> rejects other, possibly dangerous, options).
+Each option is on its own line and no quoting, escaping or stripping
+is performed on the values.
+
+Example:
+
+  target_config="\
+  --uboot=(uboot)
+  --uboot-init=setenv serverip 192.168.1.1; setenv ipaddr 192.168.1.10
+  --uboot-addr=kernel=0x8100000
+  --uboot-addr=fdt=0x83000000
+  --uboot-addr=ramdisk=0x83100000
+  "
+
+
+=back
+
+=head1 AUTHORS
+
+Michal Sojka <sojkam1@fel.cvut.cz>
+
+Latest version can be found at
+L<https://github.com/wentasah/novaboot>.
+
+=cut
+EOF