32a422d7ea223a943727927661117641e52fd5ab
[automated-distro-installer] / pxe-server
1 #!/bin/bash
2 # Copyright (C) 2016 Ian Kelling
3
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
8
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
18
19 [[ $EUID == 0 ]] || exec sudo -E "${BASH_SOURCE[0]}" "$@"
20
21 set -e; . /usr/local/lib/bash-bear; set +e
22
23 this_file="$(readlink -f -- "${BASH_SOURCE[0]}")"
24 readonly this_file this_dir="${this_file%/*}"
25 cd "$this_dir"
26 PATH="$PATH:$PWD"
27
28
29 usage() {
30 cat <<EOF
31 Usage: ${0##*/} [OPTIONS] [HOST] [TYPE]
32 Configure dnsmasq boot options and fai-chboot if appropriate. This is
33 not general purpose, it has code specific to dhcp servers I run.
34
35 Without TYPE, disable server and fai server. In that case, HOST is only
36 needed for fsf office network.
37
38 HOST Only do dhcp pxe for HOST. The hostname must be known to the dhcp
39 server to target its mac. Use "default" for all hosts.
40 Required in fsf office environment.
41
42 TYPE One of arch, parabola, plain, fai.
43
44 -a Don't setup pxe, just Wait for 2 dhcp acks, then disable the pxe
45 server after a delay. First ack is for pxe boot, 2nd ack is
46 for os boot. Sometimes on debian, there is a 3rd one shortly
47 after the 2nd. I can't remember exactly why this caused a
48 problem, but I'm hoping the sleep will take care of it.
49 -d Don't alter dhcp config. Only make sense for fai type, and on network
50 other than home or fsf, when using fai-cd, or pxe-kexec.
51 -k Pass -k to myfai-chboot.
52 --no-r Pass --no-r to myfai-chboot.
53 -r Don't redeploy fai config. For example, if there is a different host
54 that is mid-install.
55
56 -S sets FAI_ACTION=sysinfo, see myfai-chboot for more info.
57 -w Setup pxe, then wait like -a.
58 -h|--help Print help and exit
59
60
61 Note, when switching between plain and arch or parabola, you will need to
62 do something like:
63 ssh wrt
64 cd /mnt/usb
65 rm tftpboot
66 ln -s <arch/parabola/debian iso dir> tftpboot
67
68
69 Notes on debugging pxe dhcp tftp:
70
71 For debugging dhcp, add to /etc/dnsmasq.conf: log-dhcp
72
73 Newer openwrt runs dnsmasq with a whitelist of readable files and dirs:
74
75 ps ww :
76 /sbin/ujail -t 5 -n dnsmasq -u -l -r /bin/ubus -r /etc/TZ -r /etc/dnsmasq.conf -r /etc/ethers -r /etc/group -r /etc/hosts -r /etc/passwd -w /tmp/dhcp.leases -r /tmp/dnsmasq.d -r /tmp/hosts -r /tmp/resolv.conf.d -r /usr/bin/jshn -r /usr/lib/dnsmasq/dhcp-script.sh -r /usr/share/dnsmasq/dhcpbogushostname.conf -r /usr/share/dnsmasq/rfc6761.conf -r /usr/share/dnsmasq/trust-anchors.conf -r /usr/share/libubox/jshn.sh -r /var/etc/dnsmasq.conf.cfg01411c -w /var/run/dnsmasq/ -- /usr/sbin/dnsmasq -C /var/etc/dnsmasq.conf.cfg01411c -k -x /var/run/dnsmasq/dnsmasq.cfg01411c.pid
77
78 logging tftp requests:
79 /etc/default/tftpd-hpa:
80 add -vv:
81 TFTP_OPTIONS="--secure -vv"
82 jr -u tftpd-hpa -f
83
84 Note: Uses GNU getopt options parsing style
85 EOF
86 exit $1
87 }
88
89 pre="${0##*/}:"
90 m() { printf "$pre %s\n" "$*"; "$@"; }
91 e() { printf "$pre %s\n" "$*"; }
92 err() { echo "[$(date +'%Y-%m-%d %H:%M:%S%z')]: $pre: $*" >&2; }
93
94 PATH="/a/exe:$PATH"
95
96 ##### begin command line parsing ########
97
98 dhcp=true
99 redep=true
100 acks=2
101 wait=false
102 fsf_office=false
103
104 case $HOSTNAME in
105 x3|kw) fsf_office=true ;;
106 esac
107
108 chboot_args=()
109 temp=$(getopt -l no-r,help adkrSwh "$@") || usage 1
110 eval set -- "$temp"
111 while true; do
112 case $1 in
113 -a) wait=true; set=false; shift ;;
114 -d) dhcp=false; shift ;;
115 -k) chboot_args+=(-k); shift ;;
116 --no-r) chboot_args+=(--no-r); shift ;;
117 -r) redep=false; shift ;;
118 -S) chboot_args+=(-S); shift ;;
119 -w) wait=true; set=true; shift ;;
120 -h|--help) usage ;;
121 --) shift; break ;;
122 *) echo "$0: Internal error!" ; exit 1 ;;
123 esac
124 done
125
126 read -r host type <<<"$@"
127
128 case $# in
129 [01]);;
130 2)
131 case $type in
132 arch|parabola) cmd=archlike ;;
133 fai) cmd=fai ;;
134 *)
135 echo "$0: error expected type of arch|parabola|fai"
136 echo
137 usage 1
138 ;;
139 esac
140 ;;
141 *)
142 echo "$0: error: expected 0-2 arguments"
143 echo
144 usage 1
145 ;;
146 esac
147
148
149 if $wait && ! $dhcp; then
150 echo "$0: error -w conflicts with -d, choose one or other" >&2
151 exit 1
152 fi
153
154 if $fsf_office && [[ ! $host ]]; then
155 echo "$0: at fsf_office, provide HOST arg" >&2
156 exit 1
157 fi
158
159 if [[ $host && $host != default ]]; then
160 host_tag="tag:$host,"
161 fi
162
163 ##### end command line parsing ########
164
165 archlike() {
166 cat <<EOF
167 ${host_tag}209,boot/syslinux/${type}iso.cfg
168 ${host_tag}210,/${type}/
169 ${host_tag}option:bootfile-name,/${type}/boot/syslinux/lpxelinux.0
170 EOF
171 }
172
173 plain() {
174 # if arch based was used before, this additionally needs
175 # the tftp link in /mnt/usb to be changed.
176 cat <<EOF
177 ${host_tag}option:bootfile-name,pxelinux.0
178 EOF
179 }
180
181 fai() {
182 cat <<EOF
183 ${host_tag}option:bootfile-name,pxelinux.0
184 ${host_tag}option:server-ip-address,$faiserverip
185 ${host_tag}option:tftp-server,$faiserverip
186 EOF
187 # Note, previously used normal dnsmasq option, but it requires dnsmasq
188 # restart, which causes momentary dns failures, which can bork an
189 # install.
190 #
191 # dhcp-boot=${host_tag}pxelinux.0,faiserver.b8.nz,faiserver.b8.nz
192 }
193
194 ack-wait() {
195 if $fsf_office; then
196 wait_cmd="ssh tarantula.office.fsf.org tail -n0 -f /var/log/syslog"
197 else
198 wait_cmd="ssh cmc logread -f"
199 fi
200 wait_count=$1
201 if [[ $host ]]; then
202 if $fsf_office; then
203 host_regex=" $(getent hosts kw | awk '{print $1}' | sed 's/\./\\./g')"
204 else
205 host_regex=" $host"
206 fi
207 fi
208 regex=".*DHCPACK.*$host_regex\b"
209 i=0
210 while (( i != wait_count )) && read -r line; do
211 if [[ $line =~ $regex ]]; then
212 i=$((i+1))
213 echo $line
214 fi
215 done < <($wait_cmd ||:) # tail returns 2 it seems
216 m sleep 20
217 }
218
219 set-pxe() {
220 $dhcp || return 0
221 if $fsf_office; then
222 if [[ ! $cmd ]]; then
223 e "removing pxe for $host on tarantula"
224 # shellcheck disable=SC2087 # shellcheck being dumb
225 ssh tarantula.office.fsf.org bash -e <<EOF
226 sed -ri 's/^( *host +$host *\{).*/\1/' /etc/dhcp/dhcpd.conf
227 systemctl restart isc-dhcp-server
228 EOF
229 elif [[ $cmd == fai ]]; then
230 e "adding pxe for $host on tarantula"
231 # shellcheck disable=SC2087 # shellcheck being dumb
232 ssh tarantula.office.fsf.org bash -e <<EOF
233 sed -ri 's/^( *host +$host *\{).*/\1 next-server faiserver.office.fsf.org; filename "pxelinux.0";/' /etc/dhcp/dhcpd.conf
234 systemctl restart isc-dhcp-server
235 EOF
236 fi
237 else
238 e "updating dnsmasq.conf:"
239 m $cmd
240 ${cmd:-:}|ssh cmc "dd of=/var/run/dnsmasq/dhcpopts.conf; /etc/init.d/dnsmasq reload
241 $([[ $type == arch || $type == parabola ]] && echo archlike-pxe-mount)"
242 fi
243 }
244
245 type -t host &>/dev/null || sudo apt-get -y install dnsutils
246 faiserverip=$(host faiserver | sed -rn 's/^\S+ has address //p;T;q' ||:)
247 if [[ ! $faiserverip || $faiserverip =~ [[:space:]] ]]; then
248 echo "$0: error: failed to get \$faiserverip, got: $faiserverip"
249 exit 1
250 fi
251
252
253 if $set; then
254 set-pxe
255 if [[ $type == fai ]]; then
256 if $redep; then
257 m fai-redep
258 fi
259 m myfai-chboot ${chboot_args[@]} $host
260 else
261 # This will fail if faiserver is not setup, so ignore any
262 # failure and don't bother us about it.
263 m myfai-chboot &>/dev/null ||:
264 fi
265 fi
266
267 if $wait; then
268 # fai's debian jessie 8.5ish does 2 dhcp requests when booting,
269 # roughly 4 seconds apart. Earlier
270 # versions did just 1. Now testing on a vm, it does 1.
271 # bleh.
272 echo "waiting for $acks dhcp acks then disabling pxe"
273 ack-wait $acks
274 type=
275 unset cmd
276 set-pxe
277
278 # previously tried waiting for one more ack then disabling faiserver,
279 # since it can contain sensitive info, so turn it off when not in use,
280 # but disabling that for now as it's inconvenient to clean this
281 # up and run it in the background etc.
282
283 # if [[ $type == fai ]]; then
284 # echo "waiting for 1 dhcp ack then disabling fai server"
285 # ack-wait 1
286 # faiserver-disable
287 # fi
288 fi