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