· 9 years ago · Jan 28, 2017, 04:24 AM
1#!/opt/bin/bash
2# Pi-hole: A black hole for Internet advertisements
3# (c) 2015, 2016 by Jacob Salmela
4# Network-wide ad blocking via your Raspberry Pi
5# http://pi-hole.net
6# Installs Pi-hole
7#
8# Pi-hole is free software: you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published by
10# the Free Software Foundation, either version 2 of the License, or
11# (at your option) any later version.
12
13# piholeDebug.sh is not installed. It does not work because dnsmasq is in wierd places
14# updateDashboard.sh is not installed. To update pihole and dashboard use this script.
15# setupLCD.sh is not installed
16
17#TO DO
18# sqlite3
19
20#Set this to an IP different than your router and not in your dhcp range
21IPHOLE="192.168.1.254"
22
23#Set this to your tz
24TZ="America/Los_Angeles"
25
26spinner()
27{
28 local pid=$1
29 local delay="1s"
30 local spinstr='/-\|'
31 while [ "$(ps | awk '{print $1}' | grep "$pid")" ]; do
32 local temp=${spinstr#?}
33 printf " [%c] " "$spinstr"
34 local spinstr=${temp}${spinstr%"$temp"}
35 sleep ${delay}
36 printf "\b\b\b\b\b\b"
37 done
38 printf " \b\b\b\b"
39}
40
41installDependencies() {
42 echo ":::"
43 echo "::: Installing Dependencies"
44
45 PIHOLE_DEPS=(
46 bc bash curl git git-http sed rsync perl python3 python3-sqlite3 procps-ng-pgrep logrotate sqlite3-cli
47 coreutils-date coreutils-mktemp coreutils-tail coreutils-truncate net-tools-hostname
48 php5-fastcgi php5-mod-json php5-mod-openssl php5-mod-session
49 lighttpd lighttpd-mod-fastcgi lighttpd-mod-access lighttpd-mod-accesslog lighttpd-mod-expire
50 lighttpd-mod-compress lighttpd-mod-redirect lighttpd-mod-rewrite lighttpd-mod-setenv
51 )
52
53 for i in "${PIHOLE_DEPS[@]}"; do
54 opkg install "$i"
55 done
56
57 echo "!!! done."
58}
59
60createBridge() {
61echo ":::"
62echo "::: Creating Bridge Interface"
63
64FILE=/jffs/scripts/services-start
65touch "$FILE"
66chmod +x "$FILE"
67grep -q "$IPHOLE" "$FILE" || echo '
68
69#Setup bridge for PiHole
70ifconfig br0:1 '$IPHOLE' netmask 255.255.255.0 up
71
72' >> "$FILE"
73
74source "$FILE"
75
76echo "!!! done."
77}
78
79setupPhpTZ() {
80 echo ":::"
81 echo "::: Setup php.ini with correct TZ..."
82 #TZ=$(cut -f1 -d "," /opt/etc/TZ)
83 sed -i "s|.*date.timezone.*|date.timezone = $TZ|" /opt/etc/php.ini
84 echo "!!! done."
85}
86
87setupDnsmasq() {
88echo ":::"
89echo "::: Creating dnsmasq configuration"
90
91FILE=/jffs/configs/dnsmasq.conf.add
92touch "$FILE"
93grep -q "pihole" "$FILE" || echo '
94
95# Set dnsmasq configs for PiHole
96log-queries
97log-async
98log-facility=/opt/var/log/pihole.log
99addn-hosts=/opt/etc/pihole/gravity.list
100' >> "$FILE"
101
102service restart_dnsmasq >> /dev/null
103
104echo "!!! done."
105}
106
107setupLighttpd() {
108echo ":::"
109echo "::: Creating lighttpd configuration"
110
111#Setup cache dir for compress
112mkdir -p /tmp/lighttpd/compress
113sed -i 's|cache_dir|"/tmp/lighttpd"|g' /opt/etc/lighttpd/conf.d/30-compress.conf
114
115FILE=/opt/etc/lighttpd/conf.d/40-pihole.conf
116touch "$FILE"
117grep -q "pihole" "$FILE" || echo '
118
119server.bind = "'$IPHOLE'"
120server.error-handler-404 = "pihole/index.html"
121
122accesslog.filename = "/opt/var/log/lighttpd/access.log"
123accesslog.format = "%{%s}t|%V|%r|%s|%b"
124
125fastcgi.server = (
126 ".php" =>
127 ( "localhost" =>
128 ( "socket" => "/tmp/php-fcgi.sock",
129 "bin-path" => "/opt/bin/php-fcgi",
130 "max-procs" => 1,
131 "bin-environment" =>
132 ( "PHP_FCGI_CHILDREN" => "2",
133 "PHP_FCGI_MAX_REQUESTS" => "1000"
134 )
135 )
136 )
137)
138
139# If the URL starts with /admin, it is the Web interface
140$HTTP["url"] =~ "^/admin/" {
141 # Create a response header for debugging using curl -I
142 setenv.add-response-header = (
143 "X-Pi-hole" => "The Pi-hole Web interface is working!",
144 "X-Frame-Options" => "DENY"
145 )
146}
147
148# If the URL does not start with /admin, then it is a query for an ad domain
149$HTTP["url"] =~ "^(?!/admin)/.*" {
150 # Create a response header for debugging using curl -I
151 setenv.add-response-header = ( "X-Pi-hole" => "A black hole for Internet advertisements." )
152 # rewrite only js requests
153 url.rewrite = ("(.*).js" => "pihole/index.js")
154}
155
156$HTTP["host"] =~ "ads.hulu.com|ads-v-darwin.hulu.com|ads-e-darwin.hulu.com" {
157 url.redirect = ( "^/published/(.*)" => "http://192.168.1.1:8200/MediaItems/pi-hole.mov")
158}
159
160' >> "$FILE"
161
162/opt/etc/init.d/S80lighttpd restart
163
164echo "!!! done."
165}
166
167webInterfaceGitUrl="https://github.com/pi-hole/AdminLTE.git"
168webInterfaceGitBranch="devel"
169webInterfaceDir="/opt/etc/.pihole_admin"
170piholeGitUrl="https://github.com/pi-hole/pi-hole.git"
171piholeGitBranch="master"
172piholeFilesDir="/opt/etc/.pihole"
173
174getGitFiles() {
175 # Setup git repos for base files and web admin
176 echo ":::"
177 echo "::: Checking for existing base files..."
178 if is_repo ${piholeFilesDir}; then
179 make_repo ${piholeFilesDir} ${piholeGitUrl}
180 else
181 update_repo ${piholeFilesDir} ${piholeGitBranch}
182 fi
183
184 echo ":::"
185 echo "::: Checking for existing web interface..."
186 if is_repo ${webInterfaceDir}; then
187 make_repo ${webInterfaceDir} ${webInterfaceGitUrl}
188 else
189 update_repo ${webInterfaceDir} ${webInterfaceGitBranch}
190 fi
191}
192
193is_repo() {
194 # If the directory does not have a .git folder it is not a repo
195 echo -n "::: Checking $1 is a repo..."
196 if [ -d "$1/.git" ]; then
197 echo " OK!"
198 return 1
199 fi
200 echo " not found!!"
201 return 0
202}
203
204make_repo() {
205 # Remove the non-repod interface and clone the interface
206 echo -n "::: Cloning $2 into $1..."
207 rm -rf "$1"
208 git clone -q "$2" "$1" > /dev/null & spinner $!
209 echo " done!"
210}
211
212update_repo() {
213 # Pull the latest commits
214 echo "::: Updating repo in $1..."
215 cd "$1" || exit
216 git checkout "$2"
217 git pull -q > /dev/null & spinner $!
218 echo " done!"
219}
220
221installScripts() {
222 # Install the scripts from /opt/etc/.pihole to their various locations
223 echo ":::"
224 echo "::: Installing scripts to /opt/pihole..."
225
226 mkdir -p /opt/pihole
227
228 cp /opt/etc/.pihole/pihole /opt/pihole/pihole
229 cp /opt/etc/.pihole/gravity.sh /opt/pihole/gravity.sh
230 cp /opt/etc/.pihole/advanced/Scripts/chronometer.sh /opt/pihole/chronometer.sh
231 cp /opt/etc/.pihole/advanced/Scripts/whitelist.sh /opt/pihole/whitelist.sh
232 cp /opt/etc/.pihole/advanced/Scripts/blacklist.sh /opt/pihole/blacklist.sh
233 cp /opt/etc/.pihole/advanced/Scripts/version.sh /opt/pihole/version.sh
234
235 #make everything executable
236 chmod +x /opt/pihole/*.sh
237
238 #everything in /var/www/html is actually in /opt/share/www
239 sed -i 's|/var/www/html|/opt/share/www|g' /opt/pihole/*
240
241 #everything in /etc is actually in /opt/etc
242 sed -i 's|\\\/etc\\\/|\\\/opt\\\/etc\\\/|g' /opt/pihole/*
243 sed -i 's|/etc/|/opt/etc/|g' /opt/pihole/*
244
245 #everything in /var is actually in /opt/var
246 sed -i 's|\\\/var\\\/|\\\/opt\\\/var\\\/|g' /opt/pihole/*
247 sed -i 's|/var/|/opt/var/|g' /opt/pihole/*
248
249 #bash is in /opt/bin/bash
250 sed -i 's|/bin/bash|/opt/bin/bash|g' /opt/pihole/*
251
252 #in the gravity.sh script don't run gravity_reload function.
253 #it doesn't work with our dnsmasq setup
254 sed -e '/^gravity_reload/ s/^#*/#/' -i /opt/pihole/gravity.sh
255 #instead just restart dnsmasq
256 echo 'service restart_dnsmasq' >> gravity.sh
257
258 #remove functionality from pihole that does not work correctly
259 sed -e '/-ud.*updateDashboard/ s/^#*/#/' -i /opt/pihole/pihole
260 sed -e '/-up.*updatePihole/ s/^#*/#/' -i /opt/pihole/pihole
261 sed -e '/-s.*setupLCD/ s/^#*/#/' -i /opt/pihole/pihole
262 sed -e '/-d.*debug/ s/^#*/#/' -i /opt/pihole/pihole
263
264 #fix version.sh
265 sed -i 's/grep.*)/grep tag_name | cut -d ":" -f 2 | tr -d "\\\" ,")/g' /opt/pihole/version.sh
266 sed -i 's|/opt/share/www/admin|/opt/etc/.pihole_admin|g' /opt/pihole/version.sh
267
268 #link pihole to something in our path
269 ln -sf /opt/pihole/pihole /opt/usr/sbin/pihole
270
271 echo "!!! done."
272}
273
274installAdmin() {
275 echo ":::"
276 echo -n "::: Installing Admin to /opt/share/www/admin..."
277
278 mkdir -p /opt/share/www/admin
279 rsync -a --exclude=".git*" /opt/etc/.pihole_admin/ /opt/share/www/admin/ > /dev/null & spinner $!
280
281 #everything in /etc is actually in /opt/etc
282 find /opt/share/www/admin/ -type f -exec sed -i -e 's|/etc|/opt/etc|g' {} \; > /dev/null & spinner $!
283
284 #everything in /var is actually in /opt/var
285 find /opt/share/www/admin/ -type f -exec sed -i -e 's|/var|/opt/var|g' {} \; > /dev/null & spinner $!
286
287 #fix bug in script data.php
288 sed -i '/function getAllQueries() {/a \ \ \ \ \ \ \ \ \$status = ""; ' /opt/share/www/admin/data.php
289
290 #enable php debug
291 echo "error_reporting = E_ALL" > /opt/share/www/admin/.user.ini
292 echo "display_errors = On" >> /opt/share/www/admin/.user.ini
293 echo "html_errors = On" >> /opt/share/www/admin/.user.ini
294 echo "display_startup_errors = On" >> /opt/share/www/admin/.user.ini
295 echo "log_errors = On" >> /opt/share/www/admin/.user.ini
296
297 echo "!!! done."
298}
299
300installPiholeMov() {
301 echo ":::"
302 echo -n "::: Installing pi-hole movie..."
303 curl -s -o /opt/pihole/pi-hole.mov http://jacobsalmela.com/wp-content/uploads/2014/10/pi-hole.mov > /dev/null & spinner $!
304 echo " done."
305}
306
307createPiholeIpFile() {
308 echo ":::"
309 echo "::: Create PiHole Ip file..."
310 mkdir -p /opt/etc/pihole
311 echo "$IPHOLE" > /opt/etc/pihole/piholeIP
312 echo "!!! done."
313}
314
315createPiholeSetupVarsFile() {
316 echo ":::"
317 echo "::: Create PiHole setupVars.conf file..."
318 mkdir -p /opt/etc/pihole
319
320 echo 'IPv4addr="'$IPHOLE'"' > /opt/etc/pihole/setupVars.conf
321 echo 'piholeInterface="br0:0"' >> /opt/etc/pihole/setupVars.conf
322 echo 'piholeIPv6=""' >> /opt/etc/pihole/setupVars.conf
323 echo 'piholeDNS1=""' >> /opt/etc/pihole/setupVars.conf
324 echo 'piholeDNS2=""' >> /opt/etc/pihole/setupVars.conf
325
326 echo "!!! done."
327}
328
329createDummyHostnameFile() {
330 echo ":::"
331 echo "::: Create dummy Host file..."
332 echo "pi.hole" > /opt/etc/hostname
333 echo "!!! done."
334}
335
336createLogFile() {
337 # Create logfiles if necessary
338 echo ":::"
339 echo "::: Creating log file and changing owner to nobody..."
340 touch /opt/var/log/pihole.log
341 chmod 644 /opt/var/log/pihole.log
342 chown nobody:root /opt/var/log/pihole.log
343 echo "::: done!"
344}
345
346setupLogrotate() {
347echo ":::"
348echo "::: configure logrotate for dnsmasq..."
349
350FILE=/opt/etc/logrotate.d/pihole
351touch "$FILE"
352grep -q "pihole" "$FILE" || echo '
353/opt/var/log/pihole.log {
354 daily
355 missingok
356 rotate 2
357 notifempty
358 compress
359 sharedscripts
360 postrotate
361 [ ! -f /var/run/dnsmasq.pid ] || kill -USR2 $(cat /var/run/dnsmasq.pid)
362 endscript
363 create 0644 nobody root
364}
365' > "$FILE"
366chmod 0644 "$FILE"
367
368FILE=/jffs/scripts/init-start
369touch "$FILE"
370chmod +x "$FILE"
371grep -q "logrotate" "$FILE" || echo '
372
373# logrotate
374cru a logrotate "0 0 * * * /opt/sbin/logrotate -f /opt/etc/logrotate.conf &>/dev/null"
375
376' >> "$FILE"
377
378echo "!!! done"
379}
380
381createPiholeDb() {
382echo ":::"
383echo "::: create pihole db..."
384
385piholeDb=/opt/etc/pihole/pihole.db
386
387if [ -e $piholeDb ]
388then
389 echo "::: db already exists..."
390else
391 tmp="/tmp/pihole.str"
392
393 echo '
394 create table ad_domains (domain varchar);
395 create table queries (dt datetime, domain varchar, ip varchar(15));
396 create table queries_hour(dt datetime, domain varchar, ip varchar(15), count integer);
397 create table queries_day(dt datetime, domain varchar, ip varchar(15), count integer);
398 create table queries_month(dt datetime, domain varchar, ip varchar(15), count integer);
399 create table queries_year(dt datetime, domain varchar, ip varchar(15), count integer);
400 CREATE UNIQUE INDEX ad_domains_index ON ad_domains (domain);
401 CREATE UNIQUE INDEX queries_hour_index ON queries_hour (dt, domain, ip);
402 CREATE UNIQUE INDEX queries_day_index ON queries_day (dt, domain, ip);
403 CREATE UNIQUE INDEX queries_month_index ON queries_month (dt, domain, ip);
404 CREATE UNIQUE INDEX queries_year_index ON queries_year (dt, domain, ip);
405 ' > $tmp
406
407 sqlite3 $piholeDb < $tmp;
408 rm -f $tmp
409fi
410echo "!!! done"
411}
412
413installPiholeWeb() {
414 # Install the web interface
415 echo ":::"
416 echo "::: Installing pihole custom index page..."
417 mkdir -p /opt/share/www/pihole
418 cp /opt/etc/.pihole/advanced/index.* /opt/share/www/pihole/.
419 echo "!!! done"
420}
421
422installCron() {
423echo ":::"
424echo "::: Installing Cron Jobs"
425
426FILE=/jffs/scripts/init-start
427touch "$FILE"
428chmod +x "$FILE"
429grep -q "pihole" "$FILE" || echo '
430
431# Pi-hole: Update the ad sources once a week on Sunday at 01:59
432cru a UpdateGravity "59 1 * * 7 /opt/pihole/pihole updateGravity"
433
434' >> "$FILE"
435
436echo "!!! done."
437}
438
439runGravity() {
440 # Rub gravity.sh to build blacklists
441 echo ":::"
442 echo "::: Preparing to run gravity.sh to refresh hosts..."
443 if ls /opt/etc/pihole/list* 1> /dev/null 2>&1; then
444 echo "::: Cleaning up previous install (preserving whitelist/blacklist)"
445 rm /opt/etc/pihole/list.*
446 fi
447 echo "::: Running gravity.sh"
448 /opt/pihole/gravity.sh
449}
450
451installPiHole() {
452 installDependencies
453 createBridge
454 setupPhpTZ
455 setupDnsmasq
456 setupLighttpd
457 getGitFiles
458 installScripts
459 installAdmin
460 installPiholeMov
461 createPiholeDb
462 createPiholeIpFile
463 createPiholeSetupVarsFile
464 createDummyHostnameFile
465 createLogFile
466 setupLogrotate
467 installPiholeWeb
468 installCron
469 runGravity
470
471 echo "::: View the web interface at http://pi.hole/admin or http://$IPHOLE/admin"
472}
473
474updatePihole() {
475 installDependencies
476 getGitFiles
477 installScripts
478 installAdmin
479 installPiholeMov
480 installPiholeWeb
481 runGravity
482}
483
484
485function helpFunc {
486 echo "::: Install PiHole!"
487 echo ":::"
488 echo "::: Options:"
489 echo "::: -i, install"
490 echo "::: -u, update"
491 exit 1
492}
493
494if [[ $# = 0 ]]; then
495 helpFunc
496fi
497
498# Handle redirecting to specific functions based on arguments
499case "$1" in
500"-i" | "install" ) installPiHole;;
501"-u" | "install" ) updatePiHole;;
502* ) helpFunc;;
503esac