]> rtime.felk.cvut.cz Git - novaboot.git/blob - server/novaboot-shell
tests: Make some server tests more readable
[novaboot.git] / server / novaboot-shell
1 #!/bin/sh
2
3 set -e
4
5 die() {
6     echo >&2 "novaboot-shell: $*"
7     exit 1
8 }
9
10 print_help() {
11     cat <<EOF
12 Target commands:
13 - console (default command)
14 - reset
15 - on
16 - off
17 - rsync ...
18 - get-config
19 - ssh [options]
20
21 Management commands:
22 - help
23 EOF
24
25     if [ "$NB_ADMIN" ]; then
26         cat <<EOF
27 - add-key
28 - shell (use with ssh -t)
29 EOF
30     fi
31     exit 0
32 }
33
34 add_key() {
35     local user
36     [ "$NB_ADMIN" ] || return 1
37
38     case $# in
39         0) die "Usage: ssh ... add-key USERNAME < id_rsa.pub";;
40         1) break;;
41         *) die "User name must not contain spaces: $*";;
42     esac
43     user="$1"
44     key=$(cat)
45
46     tmp=$(mktemp ~/.ssh/authorized_keys.XXXXXXXX)
47     {
48         cat ~/.ssh/authorized_keys
49         echo "command=\"user $user\" $key"
50     } | sort -u > $tmp
51
52     mv $tmp ~/.ssh/authorized_keys
53 }
54
55 exec_shell() {
56     [ "$NB_ADMIN" ] || die "Permission denied"
57     if ! tty > /dev/null; then
58         echo "novaboot-shell: Consider starting the shell with 'ssh -t'"
59     fi
60     exec /bin/bash || exec /bin/sh
61 }
62
63 lock_queue() {
64     lslocks | awk '{ if ($9 == "'"$RUN_DIR"'") { print $2 } }'
65 }
66
67 print_queue() {
68     local queue
69
70     queue=$(
71         for pid in $(lock_queue); do
72             echo $pid $(sed --null-data -ne '/^NOVABOOT_ID=/ s///p' /proc/$pid/environ)
73         done | sort)
74     if [ "$queue" ]; then
75         echo "Target is occupied by:"
76         ( echo "PID USER LOGIN_TIME FROM"; echo "$queue" ) | column -t
77     fi
78 }
79
80 locked() {
81     print_queue
82     exec flock --no-fork "$RUN_DIR" "$@"
83 }
84
85 unlocked() {
86     exec "$@"
87 }
88
89 read_config() {
90     . "${NOVABOOT_SHELL_CONFIG:-$HOME/.novaboot-shell}"
91 }
92
93 # run_subcommand should be called only after permission checks and/or locking
94 run_subcommand() {
95     read_config
96     case "$*" in
97         "console")
98             trap "rm -f $RUN_DIR/ppid" EXIT
99             echo $NOVABOOT_PPID > $RUN_DIR/ppid
100             echo 'novaboot-shell: Connected'
101             # TODO: $reset_begin_cmd
102             eval exec "${console_cmd:?}";;
103         "reset")
104             eval exec "${reset_cmd:?}";;
105         "rsync --server "*" . .")
106             if ! [ $# -eq 5 -o \( $# -eq 6 -a "$4" = '--log-format=X' \) ]; then
107                 die "Unexpected rsync invocation: $*"
108             fi
109             mkdir -p "$HOME/tftproot"
110             cd "$HOME/tftproot"
111             exec "$@";;
112         "on")
113             eval exec "${on_cmd:?}";;
114         "off")
115             eval exec "${off_cmd:?}";;
116         "ssh"|"ssh "*)
117             shift
118             # TODO: sanitize ssh arguments
119             exec ssh "$@" "${ssh_dest:?}";;
120         *)
121             die "Unsupported command";;
122     esac
123 }
124
125 main() {
126     if [ "$1" = "-c" ]; then
127         set -- $2
128     elif [ $# -gt 0 ]; then
129         die "Permission denied"
130     fi
131
132     NB_ADMIN=
133     if [ "$1" = "user" ]; then
134         # Get user name encoded in ~/.ssh/authorized_keys
135         NB_USER="$2";
136         [ "$3" = "admin" ] && NB_ADMIN=1
137         set -- $SSH_ORIGINAL_COMMAND
138     fi
139
140     IP=${SSH_CONNECTION%% *}
141     if [ "$IP" ]; then
142         HOST=$(getent hosts $IP) || HOST=$IP
143     else
144         HOST=localhost
145     fi
146     REMOTE=${HOST##* }
147     DATE=$(LANG=C date +'%F_%T')
148     export NOVABOOT_ID="${NB_USER:-?} $DATE ${REMOTE}"
149     export NOVABOOT_PPID=$PPID
150
151     mkdir -p "$RUN_DIR"
152
153     case "$1" in
154         # Commands allowed at any time
155         "console"|"") locked $0 console;;
156         "get-config") read_config && echo -n "${target_config}"; exit;;
157         "add-key") shift; add_key "$@"; exit;;
158         "shell") exec_shell; exit;;
159         "help") print_help;;
160
161         # Commands allowed only when nobody or the same user is connected
162         # to the console. "The same user" means that we were executed by
163         # the same sshd process that has the lock. This is ensured by
164         # using SSH connection sharing on client side.
165         reset | rsync | on | off | ssh)
166             ALLOWED_PPID=$(cat $RUN_DIR/ppid 2>/dev/null || :)
167             if [ "$PPID" -eq "${ALLOWED_PPID:-0}" ]; then run=unlocked; else run=locked; fi
168             $run $0 "$@";;
169         *)
170             echo >&2 "novaboot-shell: Command not allowed: $*"
171             logger -p error "novaboot-shell: Command not allowed: $*"
172             exit 1;;
173     esac
174 }
175
176 RUN_DIR="$HOME"
177
178 if [ -z "$NOVABOOT_ID" ]; then
179     main "$@"
180 else
181     run_subcommand "$@"
182 fi
183 exit
184
185 : <<EOF
186 =encoding utf8
187
188 =head1 NAME
189
190 novaboot-shell - provides novaboot with unified SSH-based interface for controlling target hardware
191
192 =head1 SYNOPSIS
193
194 B<novaboot-shell> -c "[command [arguments...]]"
195
196 B<novaboot-shell> [command [arguments...]]
197
198 B<ssh target@server> [command [arguments...]]
199
200 =head1 DESCRIPTION
201
202 B<novaboot-shell> provides L<novaboot(1)> with a unified SSH-based
203 interface for controlling the target hardware. This simplifies
204 client-side configuration, because clients typically need only the
205 I<--ssh=...> option. B<novaboot-shell> is typically configured as a
206 login shell of special user accounts associated with the target
207 hardware (as set by L<adduser-novaboot(8)>). It ensures that users can
208 perform only a limited set of actions (see L</COMMANDS> below) with
209 the target and have no shell access on the server.
210
211 =head1 COMMANDS
212
213 =over 8
214
215 =item console
216
217 Connect to target console (usually serial line). When somebody is
218 connected to the console, other users are blocked from controlling the
219 target. Blocked users see a message indicating who blocks them.
220
221 The user connected to the console is able to invoke other commands
222 such as L</reset>, but only when the command is invoked via the same
223 SSH connection. This can be accomplished by using SSH connection
224 sharing, which is what L<novaboot(1)> uses (see I<-M> and I<-S> in
225 L<ssh(1)>).
226
227 This is the default command when no command is specified on command
228 line.
229
230 =item reset
231
232 Reset the target hardware.
233
234 =item on
235
236 Power on the target hardware.
237
238 =item off
239
240 Power off the target hardware.
241
242 =item rsync [...]
243
244 This command is not meant to be invoked directly by the user. It
245 allows using L<rsync(1)> to copy files to the target, perhaps for TFTP
246 server. The rsync command must be invoked as: C<rsync ...
247 target@server:>, i.e. without specifying destination path. The files
248 will be stored into I<$HOME/tftproot>.
249
250 =item user <uernamename> [admin]
251
252 User command is meant to be used with C<command=> option in SSH's
253 L<authorized_keys(5)> file. It allows the shell to display
254 human-readable names when printing information about who blocks the
255 target. Then, the real command is taken from SSH_ORIGINAL_COMMAND
256 environment variable.
257
258 When "admin" is specified after the user name, this user is considered
259 an administrator and is allowed to run L</add-key> and L</shell>
260 commands.
261
262 =item get-config
263
264 Prints novaboot configuration options needed for the target. One
265 option per line.
266
267 =back
268
269 =head2 Administration commands
270
271 Only administrators (see L</user>) are allowed to execute these
272 commands.
273
274 =over 8
275
276 =item add-key <username>
277
278 Reads the SSH public key from standard input and adds it into in
279 F<~/.ssh/authorized_keys>.
280
281 Example: C<ssh target@server add-key johndoe < john_rsa.pub>
282
283 =item shell
284
285 Runs shell on the server. Useful for editing configuration file. It is
286 better used with allocated pseudo-terminal.
287
288 Example: C<ssh -t target@server shell>
289
290 =back
291
292 =head1 CONFIGURATION FILE
293
294 B<novaboot-shell> reads configuration file from
295 F<$HOME/.novaboot-shell>. It should define values for the following
296 variables in the SH syntax.
297
298 =over 8
299
300 =item console_cmd
301
302 Command to C<exec> that connects to target's console.
303
304 =item reset_cmd
305
306 Command to C<exec> that resets the Target.
307
308 =item on_cmd
309
310 Command to C<exec> that powers the target on.
311
312 =item off_cmd
313
314 Command to C<exec> that powers the target off.
315
316 =item target_config
317
318 Novaboot command line options that specify which boot loader is used
319 by the target (L<novaboot(1)> rejects other, possibly dangerous, options).
320 Each option is on its own line and no quoting, escaping or stripping
321 is performed on the values.
322
323 Example:
324
325   target_config="\
326   --uboot=(uboot)
327   --uboot-init=setenv serverip 192.168.1.1; setenv ipaddr 192.168.1.10
328   --uboot-addr=kernel=0x8100000
329   --uboot-addr=fdt=0x83000000
330   --uboot-addr=ramdisk=0x83100000
331   "
332
333
334 =back
335
336 =head1 AUTHORS
337
338 Michal Sojka <sojkam1@fel.cvut.cz>
339
340 Latest version can be found at
341 L<https://github.com/wentasah/novaboot>.
342
343 =cut
344 EOF