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