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