-
Notifications
You must be signed in to change notification settings - Fork 161
/
read-only-fs.sh
executable file
·324 lines (286 loc) · 9.45 KB
/
read-only-fs.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
#!/bin/bash
echo "NEWS: see https://learn.adafruit.com/read-only-raspberry-pi"
echo "This script is deprecated, with better functionality now in"
echo "the OS without hacks. It's being kept temporarily for reference."
echo "You can edit this part out if you really need to run it."
exit 0
# CREDIT TO THESE TUTORIALS:
# petr.io/en/blog/2015/11/09/read-only-raspberry-pi-with-jessie
# hallard.me/raspberry-pi-read-only
# k3a.me/how-to-make-raspberrypi-truly-read-only-reliable-and-trouble-free
if [ $(id -u) -ne 0 ]; then
echo "Installer must be run as root."
echo "Try 'sudo bash $0'"
exit 1
fi
clear
echo "This script configures a Raspberry Pi"
echo "SD card to boot into read-only mode,"
echo "obviating need for clean shutdown."
echo "NO FILES ON THE CARD CAN BE CHANGED"
echo "WHEN PI IS BOOTED IN THIS STATE. Either"
echo "the filesystems must be remounted in"
echo "read/write mode, card must be mounted"
echo "R/W on another system, or an optional"
echo "jumper can be used to enable read/write"
echo "on boot."
echo
echo "Links to original tutorials are in"
echo "script source. THIS IS A ONE-WAY"
echo "OPERATION. THERE IS NO SCRIPT TO"
echo "REVERSE THIS SETUP! ALL other system"
echo "config should be complete before using"
echo "this script. MAKE A BACKUP FIRST."
echo
echo "Run time ~5 minutes. Reboot required."
echo
echo -n "CONTINUE? [y/N] "
read
if [[ ! "$REPLY" =~ ^(yes|y|Y)$ ]]; then
echo "Canceled."
exit 0
fi
# FEATURE PROMPTS ----------------------------------------------------------
# Installation doesn't begin until after all user input is taken.
INSTALL_RW_JUMPER=0
INSTALL_HALT=0
INSTALL_WATCHDOG=0
# Given a list of strings representing options, display each option
# preceded by a number (1 to N), display a prompt, check input until
# a valid number within the selection range is entered.
selectN() {
for ((i=1; i<=$#; i++)); do
echo $i. ${!i}
done
echo
REPLY=""
while :
do
echo -n "SELECT 1-$#: "
read
if [[ $REPLY -ge 1 ]] && [[ $REPLY -le $# ]]; then
return $REPLY
fi
done
}
SYS_TYPES=(Pi\ 3\ /\ Pi\ Zero\ W All\ other\ models)
WATCHDOG_MODULES=(bcm2835_wdog bcm2708_wdog)
OPTION_NAMES=(NO YES)
echo -n "Enable boot-time read/write jumper? [y/N] "
read
if [[ "$REPLY" =~ (yes|y|Y)$ ]]; then
INSTALL_RW_JUMPER=1
echo -n "GPIO pin for R/W jumper: "
read
RW_PIN=$REPLY
fi
echo -n "Install GPIO-halt utility? [y/N] "
read
if [[ "$REPLY" =~ (yes|y|Y)$ ]]; then
INSTALL_HALT=1
echo -n "GPIO pin for halt button: "
read
HALT_PIN=$REPLY
fi
echo -n "Enable kernel panic watchdog? [y/N] "
read
if [[ "$REPLY" =~ (yes|y|Y)$ ]]; then
INSTALL_WATCHDOG=1
echo "Target system type:"
selectN "${SYS_TYPES[0]}" \
"${SYS_TYPES[1]}"
WD_TARGET=$?
fi
# VERIFY SELECTIONS BEFORE CONTINUING --------------------------------------
echo
if [ $INSTALL_RW_JUMPER -eq 1 ]; then
echo "Boot-time R/W jumper: YES (GPIO$RW_PIN)"
else
echo "Boot-time R/W jumper: NO"
fi
if [ $INSTALL_HALT -eq 1 ]; then
echo "Install GPIO-halt: YES (GPIO$HALT_PIN)"
else
echo "Install GPIO-halt: NO"
fi
if [ $INSTALL_WATCHDOG -eq 1 ]; then
echo "Enable watchdog: YES (${SYS_TYPES[WD_TARGET-1]})"
else
echo "Enable watchdog: NO"
fi
echo
echo -n "CONTINUE? [y/N] "
read
if [[ ! "$REPLY" =~ ^(yes|y|Y)$ ]]; then
echo "Canceled."
exit 0
fi
# START INSTALL ------------------------------------------------------------
# All selections have been validated at this point...
# Given a filename, a regex pattern to match and a replacement string:
# Replace string if found, else no change.
# (# $1 = filename, $2 = pattern to match, $3 = replacement)
replace() {
grep $2 $1 >/dev/null
if [ $? -eq 0 ]; then
# Pattern found; replace in file
sed -i "s/$2/$3/g" $1 >/dev/null
fi
}
# Given a filename, a regex pattern to match and a replacement string:
# If found, perform replacement, else append file w/replacement on new line.
replaceAppend() {
grep $2 $1 >/dev/null
if [ $? -eq 0 ]; then
# Pattern found; replace in file
sed -i "s/$2/$3/g" $1 >/dev/null
else
# Not found; append on new line (silently)
echo $3 | sudo tee -a $1 >/dev/null
fi
}
# Given a filename, a regex pattern to match and a string:
# If found, no change, else append file with string on new line.
append1() {
grep $2 $1 >/dev/null
if [ $? -ne 0 ]; then
# Not found; append on new line (silently)
echo $3 | sudo tee -a $1 >/dev/null
fi
}
# Given a filename, a regex pattern to match and a string:
# If found, no change, else append space + string to last line --
# this is used for the single-line /boot/cmdline.txt file.
append2() {
grep $2 $1 >/dev/null
if [ $? -ne 0 ]; then
# Not found; insert in file before EOF
sed -i "s/\'/ $3/g" $1 >/dev/null
fi
}
echo
echo "Starting installation..."
echo "Updating package index files..."
apt-get update
echo "Removing unwanted packages..."
#apt-get remove -y --force-yes --purge triggerhappy logrotate dbus \
# dphys-swapfile xserver-common lightdm fake-hwclock
# Let's keep dbus...that includes avahi-daemon, a la 'raspberrypi.local',
# also keeping xserver & lightdm for GUI login (WIP, not working yet)
apt-get remove -y --force-yes --purge triggerhappy logrotate \
dphys-swapfile fake-hwclock
apt-get -y --force-yes autoremove --purge
# Replace log management with busybox (use logread if needed)
echo "Installing ntp and busybox-syslogd..."
apt-get -y --force-yes install ntp busybox-syslogd; dpkg --purge rsyslog
echo "Configuring system..."
# Install boot-time R/W jumper test if requested
GPIOTEST="gpio -g mode $RW_PIN up\n\
if [ \`gpio -g read $RW_PIN\` -eq 0 ] ; then\n\
\tmount -o remount,rw \/\n\
\tmount -o remount,rw \/boot\n\
fi\n"
if [ $INSTALL_RW_JUMPER -ne 0 ]; then
apt-get install -y --force-yes wiringpi
# Check if already present in rc.local:
grep "gpio -g read" /etc/rc.local >/dev/null
if [ $? -eq 0 ]; then
# Already there, but make sure pin is correct:
sed -i "s/^.*gpio\ -g\ read.*$/$GPIOTEST/g" /etc/rc.local >/dev/null
else
# Not there, insert before final 'exit 0'
sed -i "s/^exit 0/$GPIOTEST\\nexit 0/g" /etc/rc.local >/dev/null
fi
fi
# Install watchdog if requested
if [ $INSTALL_WATCHDOG -ne 0 ]; then
apt-get install -y --force-yes watchdog
# $MODULE is specific watchdog module name
MODULE=${WATCHDOG_MODULES[($WD_TARGET-1)]}
# Add to /etc/modules, update watchdog config file
append1 /etc/modules $MODULE $MODULE
replace /etc/watchdog.conf "#watchdog-device" "watchdog-device"
replace /etc/watchdog.conf "#max-load-1" "max-load-1"
# Start watchdog at system start and start right away
# Raspbian Stretch needs this package installed first
apt-get install -y --force-yes insserv
insserv watchdog; /etc/init.d/watchdog start
# Additional settings needed on Jessie
append1 /lib/systemd/system/watchdog.service "WantedBy" "WantedBy=multi-user.target"
systemctl enable watchdog
# Set up automatic reboot in sysctl.conf
replaceAppend /etc/sysctl.conf "^.*kernel.panic.*$" "kernel.panic = 10"
fi
# Install gpio-halt if requested
if [ $INSTALL_HALT -ne 0 ]; then
apt-get install -y --force-yes wiringpi
echo "Installing gpio-halt in /usr/local/bin..."
cd /tmp
curl -LO https://github.com/adafruit/Adafruit-GPIO-Halt/archive/master.zip
unzip master.zip
cd Adafruit-GPIO-Halt-master
make
mv gpio-halt /usr/local/bin
cd ..
rm -rf Adafruit-GPIO-Halt-master
# Add gpio-halt to /rc.local:
grep gpio-halt /etc/rc.local >/dev/null
if [ $? -eq 0 ]; then
# gpio-halt already in rc.local, but make sure correct:
sed -i "s/^.*gpio-halt.*$/\/usr\/local\/bin\/gpio-halt $HALT_PIN \&/g" /etc/rc.local >/dev/null
else
# Insert gpio-halt into rc.local before final 'exit 0'
sed -i "s/^exit 0/\/usr\/local\/bin\/gpio-halt $HALT_PIN \&\\nexit 0/g" /etc/rc.local >/dev/null
fi
fi
# Add fastboot, noswap and/or ro to end of /boot/cmdline.txt
append2 /boot/cmdline.txt fastboot fastboot
append2 /boot/cmdline.txt noswap noswap
append2 /boot/cmdline.txt ro^o^t ro
# Move /var/spool to /tmp
rm -rf /var/spool
ln -s /tmp /var/spool
# Move /var/lib/lightdm and /var/cache/lightdm to /tmp
rm -rf /var/lib/lightdm
rm -rf /var/cache/lightdm
ln -s /tmp /var/lib/lightdm
ln -s /tmp /var/cache/lightdm
# Make SSH work
replaceAppend /etc/ssh/sshd_config "^.*UsePrivilegeSeparation.*$" "UsePrivilegeSeparation no"
# bbro method (not working in Jessie?):
#rmdir /var/run/sshd
#ln -s /tmp /var/run/sshd
# Change spool permissions in var.conf (rondie/Margaret fix)
replace /usr/lib/tmpfiles.d/var.conf "spool\s*0755" "spool 1777"
# Move dhcpd.resolv.conf to tmpfs
touch /tmp/dhcpcd.resolv.conf
rm /etc/resolv.conf
ln -s /tmp/dhcpcd.resolv.conf /etc/resolv.conf
# systemd-ntp must use systemwide /tmp to access /tmp/dhcpcd.resolv.conf
replace /lib/systemd/system/ntp.service "PrivateTmp=true" "PrivateTmp=false"
# Make edits to fstab
# make / ro
# tmpfs /var/log tmpfs nodev,nosuid 0 0
# tmpfs /var/tmp tmpfs nodev,nosuid 0 0
# tmpfs /tmp tmpfs nodev,nosuid 0 0
replace /etc/fstab "vfat\s*defaults\s.*" "vfat defaults,ro\t0\t0"
replace /etc/fstab "ext4\s*defaults,noatime\s.*" "ext4 defaults,noatime,ro\t0\t0"
append1 /etc/fstab "/var/log" "tmpfs /var/log tmpfs nodev,nosuid 0 0"
append1 /etc/fstab "/var/tmp" "tmpfs /var/tmp tmpfs nodev,nosuid 0 0"
append1 /etc/fstab "\s/tmp" "tmpfs /tmp tmpfs nodev,nosuid 0 0"
# Stop vim creating tmp files in ~/.viminfo (ro)
echo 'set viminfo=""' >>/etc/vim/vimrc.local
# PROMPT FOR REBOOT --------------------------------------------------------
echo "Done."
echo
echo "Settings take effect on next boot."
echo
echo -n "REBOOT NOW? [y/N] "
read
if [[ ! "$REPLY" =~ ^(yes|y|Y)$ ]]; then
echo "Exiting without reboot."
exit 0
fi
echo "Reboot started..."
reboot
exit 0