From b246dba7e75a484120739a0eb239a150bdb50bbf Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 4 Dec 2016 21:30:59 +0100 Subject: [PATCH 01/89] Rename index.html to index.php --- advanced/{index.html => index.php} | 0 advanced/lighttpd.conf.debian | 2 +- advanced/lighttpd.conf.fedora | 2 +- automated install/basic-install.sh | 16 ++++++++++------ 4 files changed, 12 insertions(+), 8 deletions(-) rename advanced/{index.html => index.php} (100%) diff --git a/advanced/index.html b/advanced/index.php similarity index 100% rename from advanced/index.html rename to advanced/index.php diff --git a/advanced/lighttpd.conf.debian b/advanced/lighttpd.conf.debian index 15821bc7..01f52a85 100644 --- a/advanced/lighttpd.conf.debian +++ b/advanced/lighttpd.conf.debian @@ -21,7 +21,7 @@ server.modules = ( ) server.document-root = "/var/www/html" -server.error-handler-404 = "pihole/index.html" +server.error-handler-404 = "pihole/index.php" server.upload-dirs = ( "/var/cache/lighttpd/uploads" ) server.errorlog = "/var/log/lighttpd/error.log" server.pid-file = "/var/run/lighttpd.pid" diff --git a/advanced/lighttpd.conf.fedora b/advanced/lighttpd.conf.fedora index 96e1311e..d2af5bd4 100644 --- a/advanced/lighttpd.conf.fedora +++ b/advanced/lighttpd.conf.fedora @@ -22,7 +22,7 @@ server.modules = ( ) server.document-root = "/var/www/html" -server.error-handler-404 = "pihole/index.html" +server.error-handler-404 = "pihole/index.php" server.upload-dirs = ( "/var/cache/lighttpd/uploads" ) server.errorlog = "/var/log/lighttpd/error.log" server.pid-file = "/var/run/lighttpd.pid" diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 33444873..8c7a6b1d 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -21,6 +21,7 @@ set -e tmpLog=/tmp/pihole-install.log instalLogLoc=/etc/pihole/install.log setupVars=/etc/pihole/setupVars.conf +lighttpdConfig=/etc/lighttpd/lighttpd.conf webInterfaceGitUrl="https://github.com/pi-hole/AdminLTE.git" webInterfaceDir="/var/www/html/admin" @@ -709,9 +710,9 @@ installConfigs() { mkdir /etc/lighttpd chown "${USER}":root /etc/lighttpd elif [ -f "/etc/lighttpd/lighttpd.conf" ]; then - mv /etc/lighttpd/lighttpd.conf /etc/lighttpd/lighttpd.conf.orig + mv ${lighttpdConfig} ${lighttpdConfig}.orig fi - cp /etc/.pihole/advanced/${LIGHTTPD_CFG} /etc/lighttpd/lighttpd.conf + cp /etc/.pihole/advanced/${LIGHTTPD_CFG} ${lighttpdConfig} mkdir -p /var/run/lighttpd chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/run/lighttpd mkdir -p /var/cache/lighttpd/compress @@ -825,11 +826,11 @@ installPiholeWeb() { echo ":::" echo "::: Installing pihole custom index page..." if [ -d "/var/www/html/pihole" ]; then - if [ -f "/var/www/html/pihole/index.html" ]; then - echo "::: Existing index.html detected, not overwriting" + if [ -f "/var/www/html/pihole/index.php" ]; then + echo "::: Existing index.php detected, not overwriting" else - echo -n "::: index.html missing, replacing... " - cp /etc/.pihole/advanced/index.html /var/www/html/pihole/ + echo -n "::: index.php missing, replacing... " + cp /etc/.pihole/advanced/index.php /var/www/html/pihole/ echo " done!" fi @@ -955,6 +956,9 @@ accountForRefactor() { sed -i 's/piholeDNS1/PIHOLE_DNS_1/g' ${setupVars} sed -i 's/piholeDNS2/PIHOLE_DNS_2/g' ${setupVars} + # Account for change in lighttpd config file + sed -i 's:pihole/index.html:pihole/index.php:' ${lighttpdConfig} + } updatePihole() { From 6e788668f9f9d63f44585aa54d8522eafc8092c3 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 4 Dec 2016 22:17:08 +0100 Subject: [PATCH 02/89] Updated index.php --- advanced/index.php | 61 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/advanced/index.php b/advanced/index.php index 3a4abe1f..41142633 100644 --- a/advanced/index.php +++ b/advanced/index.php @@ -1,7 +1,64 @@ + - - + + + + + + Website Blocked + + + + + + +
+

Website Blocked

+
+
+
Access to the following site has been blocked:
+
+
If you have an ongoing use for this website, please ask to owner of the Pi-Hole in your network to have it whitelisted.
+
+
Generated by Pi-hole
+ + From e7241a989cc95bcf3fca527322128d2d5c07a43b Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 4 Dec 2016 22:20:51 +0100 Subject: [PATCH 03/89] Include CSS file --- advanced/index.css | 212 +++++++++++++++++++++++++++++ advanced/index.php | 4 +- automated install/basic-install.sh | 8 ++ 3 files changed, 222 insertions(+), 2 deletions(-) create mode 100644 advanced/index.css diff --git a/advanced/index.css b/advanced/index.css new file mode 100644 index 00000000..b5fae08e --- /dev/null +++ b/advanced/index.css @@ -0,0 +1,212 @@ +/* CSS Reset */ +html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video { margin: 0; padding: 0; border: 0; font-size: 100%; font: inherit; vertical-align: baseline; } +article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { display: block; } +body { line-height: 1; } +ol, ul { list-style: none; } +blockquote, q { quotes: none; } +blockquote:before, blockquote:after, q:before, q:after { content: ''; content: none; } +table { border-collapse: collapse; border-spacing: 0; } +html { height: 100%; overflow-x: hidden; } + +/* General Style */ +a { color: rgba(0,60,120,0.95); text-decoration: none; } /* 1E3C5A */ +a:hover { color: rgba(210,120,0,0.95); transition-duration: .2s; } /* 255, 128, 0 */ +divs a { border-bottom: 1px dashed rgba(30,60,90,0.3); } +b { font-weight: bold; } +i { font-style: italic; } + +footer, .pre, input[type='url'], input[type='text'], .alist, .transparent td { font-family: monospace; } +body, header { background: #E1E1E1; } + +body { + background-image: url('http://firebog.net/bg.png'), -webkit-linear-gradient(top, rgba(220,220,220,0.95), rgba(240,240,240,0.95)); + background-image: url('http://firebog.net/bg.png'), linear-gradient(to bottom, rgba(220,220,220,0.95), rgba(240,240,240,0.95)); + background-attachment: fixed; + color: rgba(64,64,64,0.95); + text-shadow: 0 1px rgba(0,0,0,0.04); + font: 14px Arial, sans-serif; + line-height: 1em; +} + +header { + min-width: 320px; + width: 100%; + text-shadow: 0 1px rgba(255,255,255,0.6); + display: table; + table-layout: fixed; + border: 1px solid rgba(0,0,0,0.25); + border-top-color: rgba(255,255,255,0.85); + border-style: solid none; + background-image: -webkit-linear-gradient(top, rgba(240,240,240,0.95), rgba(220,220,220,0.95)); + background-image: linear-gradient(to bottom, rgba(240,240,240,0.95), rgba(220,220,220,0.95)); + box-shadow: 0 0 1px 1px rgba(0,0,0,0.04); +} + +header h1, header div { + display: table-cell; + color: inherit; + font-weight: bold; + vertical-align: middle; + white-space: nowrap; + overflow: hidden; + box-sizing: border-box; +} + +header h1 { + font: bold 1.6em "Trebuchet MS", sans-serif; + width: 100%; + padding: 8px 0; + text-indent: 32px; + background: url("http://firebog.net/PHV.svg") left no-repeat; +} + +header h1 a, h1 a:hover { color: inherit; } +header .alt { width: 85px; font-size: 0.8em; padding-right: 4px; text-align: right; line-height: 1.25em; } +.active { color: green; } +.inactive { color: red; } + +main { + display: block; + width: 80%; + padding: 8px; + font-size: 1em; + background-color: rgba(255,255,255,0.85); + margin: 8px auto; + box-sizing: border-box; + border: 1px solid rgba(0,0,0,0.25); + box-shadow: 0 0 1px 1px rgba(0,0,0,0.04); + line-height: 1.2em; + border-radius: 8px; +} + +h2 { /* Rgba is shared with .transparent th */ + font: 1.15em sans-serif; + background-color: rgba(255,0,0,0.4); + text-shadow: none; + line-height: 1.1em; + padding-bottom: 1px; + margin-top: 8px; + margin-bottom: 4px; + background: -webkit-linear-gradient(left, rgba(0,0,0,0.25), transparent 80%) no-repeat; + background: linear-gradient(to right, rgba(0,0,0,0.25), transparent 80%) no-repeat; + background-size: 100% 1px; + background-position: 0 17px; +} + +h2:first-child { margin-top: 0; } +h2 ~ *:not(h2) { margin-left: 4px; } +li { padding: 2px 0; } +li::before { content: "\00BB\00a0"; } +li a { position: relative; top: 1px; } /* Center bullet-point arrows */ + +/* Button Style */ +.buttons a, input, .transparent th a { /* Swapped rgba is shared with input[type='url'] */ + display: inline-block; + color: rgba(32,32,32,0.9); + font-weight: bold; + text-align: center; + cursor: pointer; + text-shadow: 0 1px rgba(255,255,255,0.2); + line-height: 0.86em; + font-size: 1em; + padding: 4px 8px; + background: #FAFAFA; + background-image: -webkit-linear-gradient(top, rgba(255,255,255,0.05), rgba(0,0,0,0.05)); + background-image: linear-gradient(to bottom, rgba(255,255,255,0.05), rgba(0,0,0,0.05)); + border: 1px solid rgba(0,0,0,0.25); + border-radius: 4px; + box-shadow: 0 1px 0 rgba(0,0,0,0.04); +} + +.buttons { white-space: nowrap; width: 100%; display: table; } +.mini a { width: 50%; } +a.safe { background-color: rgba(0,220,0,0.5); } +a.warn { background-color: rgba(220,0,0,0.5); } + +.blocked, .toggle, .sourcever, .mini, .addurl a, .editurl a { margin-top: 8px; } +.blocked a, .mini a { display: table-cell; } +.blocked a.safe { width: 100%; } + +.ministatus { text-align: center; } + +input[type='url'], input[type='text'] { /* Swapped rgba is shared with .buttons a */ + font-size: 1.15em; + padding: 2px 4px; + text-align: left; + cursor: text; + width: 100%; + box-sizing: border-box; + background-image: -webkit-linear-gradient(top, rgba(0,0,0,0.05), rgba(255,255,255,0.05)); + background-image: linear-gradient(to bottom, rgba(0,0,0,0.05), rgba(255,255,255,0.05)); +} + +input:focus { + outline: none; + box-shadow: 0 0 0 2px rgba(0,128,240,0.4); + border-radius: 4px; +} + +/* Types of text */ +.pre { white-space: pre; overflow: auto; -webkit-overflow-scrolling: touch; } +.msg { display: block; line-height: 1.2em; font-weight: bold; font-size: 1.15em; margin: 4px 8px 8px 8px; white-space: pre-line; } +.request, .finalresults, .sourcever, .invocation, .parameters, .transparent td, .actionfilecontent, .actionslist { font-size: 0.9em; line-height: 1.2em; } /* "Pre sized" */ + +.request, .finalresults, .parameters, .actionfilecontent, .actionslist { /* Dashed outline for excessive text */ + line-height: 1.2em; + padding: 4px; + border: 1px dashed rgba(0,0,0,0.25); + border-right: 0; + background-image: -webkit-linear-gradient(left, #FFF 70%, rgba(255,255,255,0)); + background-image: linear-gradient(to right, #FFF 70%, rgba(255,255,255,0)); + border-radius: 4px 0 0 4px; +} + +.parameters, .finalresults { /* Small scrollbox for excessive text */ + max-height: 116px; + overflow-y: scroll; + -webkit-overflow-scrolling: touch; +} + +.editactions { font-size: 0.8em; } +.actionslist { margin: 4px 0; } + +/* Matching actions in show-url-info */ +.transparent { width: 100%; margin: 0; } +.transparent th { display: block; box-sizing: border-box; text-align: left; padding: 2px 4px; border-radius: 4px; background-image: -webkit-linear-gradient(left, rgba(0,0,0,0.08), transparent 80%); background-image: linear-gradient(to right, rgba(0,0,0,0.08), transparent 80%); } /* Rgba is shared with h2 */ +.transparent td { margin: 4px; display: block; } +.transparent th a { display: none; position: relative; top: -1px; font-size: 0.75em; padding: 2px 5px 1px 3px; } /* Buttons 0.75 0.86 */ +.transparent tr:first-child a, .transparent tr:nth-last-child(2) a { display: inline-block; } /* Only show buttons for First/Last files */ + +footer { font-size: 0.8em; text-align: center; width: 87%; margin: 4px auto; } + +/* Specific Fixes */ +.alist br:first-child { display: none; } /* Final results is displayed in "pre format", so remove first BR */ +.filter a[href*="show-status"] { margin-left: 12px; } /* Margin for View/Edit on show-status */ + +/* Safari & iOS */ +@media screen and (min-color-index:0) and(-webkit-min-device-pixel-ratio:0) { @media { + ::-webkit-input-placeholder { position: relative; top: 3px; } + .transparent th a { padding: 2px 4px 1px 4px; } + a.safe { background-color: rgba(0,220,0,0.4); } + a.warn { background-color: rgba(220,0,0,0.4); } +}} + +@media only screen and (orientation:portrait) { + main { width: 100%; border-radius: 0; margin: 0; border-style: none; border-bottom-style: solid; } + header h1 { display: block; overflow: auto; -webkit-overflow-scrolling: touch; } + ::-webkit-input-placeholder { top: 2px; } +} + +@media only screen and (min-device-width : 320px) and (max-device-width : 667px) and (orientation : landscape) { + main { width: 100%; border-radius: 0; margin: 0; border-style: none; border-bottom-style: solid; } + h2 { padding-bottom: 2px; } /* Necessary for underline to show */ + main { font-size: 0.6em; } + ul li { font-size: 1.7em; } + ul li a { font-size: 0.6em; } + input[type='url'] { font-size: 1.85em; } + .buttons { font-size: 1.7em; } + .editactions { font-size: 1.3em; } + .request { font-size: 1.5em; } + .transparent th a { font-size: 1.35em; } + ::-webkit-input-placeholder { top: 2px; } +} \ No newline at end of file diff --git a/advanced/index.php b/advanced/index.php index 41142633..fe95782a 100644 --- a/advanced/index.php +++ b/advanced/index.php @@ -44,8 +44,8 @@ $piHoleVersion = exec('cd /etc/.pihole/ && git describe --tags --abbrev=0'); Website Blocked - - + + /admin/img/favicon.png' type='image/png'/> diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 8c7a6b1d..f48e78c8 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -842,6 +842,14 @@ installPiholeWeb() { echo " done!" fi + if [ -f "/var/www/html/pihole/index.css" ]; then + echo "::: Existing index.css detected, not overwriting" + else + echo -n "::: index.css missing, replacing... " + cp /etc/.pihole/advanced/index.css /var/www/html/pihole/ + echo " done!" + fi + else mkdir /var/www/html/pihole if [ -f /var/www/html/index.lighttpd.html ]; then From a866c4e388b060cdb8615afd4b57b81f0cf47665 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 5 Dec 2016 10:50:19 +0100 Subject: [PATCH 04/89] Place blockingpage.css (former index.css) unter admin/ so we can load it also from some blocked sub-directories --- advanced/{index.css => blockingpage.css} | 0 automated install/basic-install.sh | 6 +++--- 2 files changed, 3 insertions(+), 3 deletions(-) rename advanced/{index.css => blockingpage.css} (100%) diff --git a/advanced/index.css b/advanced/blockingpage.css similarity index 100% rename from advanced/index.css rename to advanced/blockingpage.css diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index f48e78c8..958b2d1d 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -842,11 +842,11 @@ installPiholeWeb() { echo " done!" fi - if [ -f "/var/www/html/pihole/index.css" ]; then - echo "::: Existing index.css detected, not overwriting" + if [ -f "/var/www/html/admin/blockingpage.css" ]; then + echo "::: Existing blockingpage.css detected, not overwriting" else echo -n "::: index.css missing, replacing... " - cp /etc/.pihole/advanced/index.css /var/www/html/pihole/ + cp /etc/.pihole/advanced/blockingpage.css /var/www/html/admin echo " done!" fi From 0f90671241c0ec097be0049989fca3a2dcbd7f10 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 5 Dec 2016 10:51:41 +0100 Subject: [PATCH 05/89] Updated index.php to use CSS file unter admin/ --- advanced/index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/advanced/index.php b/advanced/index.php index fe95782a..d9755dc2 100644 --- a/advanced/index.php +++ b/advanced/index.php @@ -44,7 +44,7 @@ $piHoleVersion = exec('cd /etc/.pihole/ && git describe --tags --abbrev=0'); Website Blocked - + /admin/blockingpage.css'/> /admin/img/favicon.png' type='image/png'/> From 20370d4348fee31d249bca39cc882ddb41ebebba Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 5 Dec 2016 11:26:21 +0100 Subject: [PATCH 06/89] Update CSS file --- advanced/blockingpage.css | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/advanced/blockingpage.css b/advanced/blockingpage.css index b5fae08e..f4451f28 100644 --- a/advanced/blockingpage.css +++ b/advanced/blockingpage.css @@ -15,7 +15,7 @@ divs a { border-bottom: 1px dashed rgba(30,60,90,0.3); } b { font-weight: bold; } i { font-style: italic; } -footer, .pre, input[type='url'], input[type='text'], .alist, .transparent td { font-family: monospace; } +footer, pre, input[type='url'], input[type='text'], .alist, .transparent td { font-family: monospace; } body, header { background: #E1E1E1; } body { @@ -76,7 +76,7 @@ main { border: 1px solid rgba(0,0,0,0.25); box-shadow: 0 0 1px 1px rgba(0,0,0,0.04); line-height: 1.2em; - border-radius: 8px; + border-radius: 8px; } h2 { /* Rgba is shared with .transparent th */ @@ -138,7 +138,7 @@ input[type='url'], input[type='text'] { /* Swapped rgba is shared with .buttons box-sizing: border-box; background-image: -webkit-linear-gradient(top, rgba(0,0,0,0.05), rgba(255,255,255,0.05)); background-image: linear-gradient(to bottom, rgba(0,0,0,0.05), rgba(255,255,255,0.05)); -} +} input:focus { outline: none; @@ -184,7 +184,7 @@ footer { font-size: 0.8em; text-align: center; width: 87%; margin: 4px auto; } .filter a[href*="show-status"] { margin-left: 12px; } /* Margin for View/Edit on show-status */ /* Safari & iOS */ -@media screen and (min-color-index:0) and(-webkit-min-device-pixel-ratio:0) { @media { +@media screen and (min-color-index:0) and(-webkit-min-device-pixel-ratio:0) { @media { ::-webkit-input-placeholder { position: relative; top: 3px; } .transparent th a { padding: 2px 4px 1px 4px; } a.safe { background-color: rgba(0,220,0,0.4); } @@ -197,7 +197,7 @@ footer { font-size: 0.8em; text-align: center; width: 87%; margin: 4px auto; } ::-webkit-input-placeholder { top: 2px; } } -@media only screen and (min-device-width : 320px) and (max-device-width : 667px) and (orientation : landscape) { +@media only screen and (min-device-width : 320px) and (max-device-width : 667px) and (orientation : landscape) { main { width: 100%; border-radius: 0; margin: 0; border-style: none; border-bottom-style: solid; } h2 { padding-bottom: 2px; } /* Necessary for underline to show */ main { font-size: 0.6em; } @@ -209,4 +209,4 @@ footer { font-size: 0.8em; text-align: center; width: 87%; margin: 4px auto; } .request { font-size: 1.5em; } .transparent th a { font-size: 1.35em; } ::-webkit-input-placeholder { top: 2px; } -} \ No newline at end of file +} From fc233fcdd3d74129c02f24ca3295c583e811b0c6 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 5 Dec 2016 12:10:05 +0100 Subject: [PATCH 07/89] Fixed typo --- advanced/index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/advanced/index.php b/advanced/index.php index d9755dc2..af44cd4f 100644 --- a/advanced/index.php +++ b/advanced/index.php @@ -56,7 +56,7 @@ $piHoleVersion = exec('cd /etc/.pihole/ && git describe --tags --abbrev=0');
Access to the following site has been blocked:
-
If you have an ongoing use for this website, please ask to owner of the Pi-Hole in your network to have it whitelisted.
+
If you have an ongoing use for this website, please ask the owner of the Pi-Hole in your network to have it whitelisted.
Generated by Pi-hole
From 3d6b22de6cfeac83ebf781dd5dd207b0852376df Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 5 Dec 2016 12:12:01 +0100 Subject: [PATCH 08/89] Update index.php to include more detailed output --- advanced/index.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/advanced/index.php b/advanced/index.php index af44cd4f..deb902bb 100644 --- a/advanced/index.php +++ b/advanced/index.php @@ -39,6 +39,12 @@ if (!$showPage) // Get Pi-Hole version $piHoleVersion = exec('cd /etc/.pihole/ && git describe --tags --abbrev=0'); +// Don't show the URI if it is the root directory +if($uri == "/") +{ + $uri = ""; +} + ?> @@ -57,8 +63,18 @@ $piHoleVersion = exec('cd /etc/.pihole/ && git describe --tags --abbrev=0');
Access to the following site has been blocked:
If you have an ongoing use for this website, please ask the owner of the Pi-Hole in your network to have it whitelisted.
+ + + + This page is blocked because it is explicitly contained within the following block list(s): +
Back to safety
Generated by Pi-hole
+ + + From 0d63cfd6c3ded71effe67475f56f3fc37273081e Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 5 Dec 2016 12:34:30 +0100 Subject: [PATCH 09/89] Add 1x1 pixel GIF image. Can apparently be used to trick some of the please-don't-use-adblocker pages as many of them contain code like `if ($('Ad').height() == 0) { prevent access to this page; }` --- advanced/index.php | 1 + 1 file changed, 1 insertion(+) diff --git a/advanced/index.php b/advanced/index.php index deb902bb..c51fd71c 100644 --- a/advanced/index.php +++ b/advanced/index.php @@ -30,6 +30,7 @@ if (!$showPage) + Date: Mon, 5 Dec 2016 14:39:07 +0100 Subject: [PATCH 10/89] Simplified CSS, removed external contents --- advanced/blockingpage.css | 101 +++++--------------------------------- advanced/index.php | 4 +- 2 files changed, 13 insertions(+), 92 deletions(-) diff --git a/advanced/blockingpage.css b/advanced/blockingpage.css index f4451f28..bb37939d 100644 --- a/advanced/blockingpage.css +++ b/advanced/blockingpage.css @@ -16,15 +16,14 @@ b { font-weight: bold; } i { font-style: italic; } footer, pre, input[type='url'], input[type='text'], .alist, .transparent td { font-family: monospace; } -body, header { background: #E1E1E1; } +/*body, header { background: #E1E1E1; }*/ body { - background-image: url('http://firebog.net/bg.png'), -webkit-linear-gradient(top, rgba(220,220,220,0.95), rgba(240,240,240,0.95)); - background-image: url('http://firebog.net/bg.png'), linear-gradient(to bottom, rgba(220,220,220,0.95), rgba(240,240,240,0.95)); + background-image: -webkit-linear-gradient(top, rgba(240,240,240,0.95), rgba(190,190,190,0.95)); + background-image: linear-gradient(to bottom, rgba(240,240,240,0.95), rgba(190,190,190,0.95)); background-attachment: fixed; color: rgba(64,64,64,0.95); - text-shadow: 0 1px rgba(0,0,0,0.04); - font: 14px Arial, sans-serif; + font: 14px, sans-serif; line-height: 1em; } @@ -53,11 +52,13 @@ header h1, header div { } header h1 { - font: bold 1.6em "Trebuchet MS", sans-serif; + font-size: 22px; + font-weight: bold; width: 100%; padding: 8px 0; text-indent: 32px; - background: url("http://firebog.net/PHV.svg") left no-repeat; + background: url("http://pi.hole/admin/img/logo.svg") left no-repeat; + background-size: 30px 22px; } header h1 a, h1 a:hover { color: inherit; } @@ -68,13 +69,13 @@ header .alt { width: 85px; font-size: 0.8em; padding-right: 4px; text-align: rig main { display: block; width: 80%; - padding: 8px; + padding: 10px; font-size: 1em; background-color: rgba(255,255,255,0.85); margin: 8px auto; box-sizing: border-box; border: 1px solid rgba(0,0,0,0.25); - box-shadow: 0 0 1px 1px rgba(0,0,0,0.04); + box-shadow: 4px 4px rgba(0,0,0,0.1); line-height: 1.2em; border-radius: 8px; } @@ -123,90 +124,10 @@ li a { position: relative; top: 1px; } /* Center bullet-point arrows */ a.safe { background-color: rgba(0,220,0,0.5); } a.warn { background-color: rgba(220,0,0,0.5); } -.blocked, .toggle, .sourcever, .mini, .addurl a, .editurl a { margin-top: 8px; } .blocked a, .mini a { display: table-cell; } .blocked a.safe { width: 100%; } -.ministatus { text-align: center; } - -input[type='url'], input[type='text'] { /* Swapped rgba is shared with .buttons a */ - font-size: 1.15em; - padding: 2px 4px; - text-align: left; - cursor: text; - width: 100%; - box-sizing: border-box; - background-image: -webkit-linear-gradient(top, rgba(0,0,0,0.05), rgba(255,255,255,0.05)); - background-image: linear-gradient(to bottom, rgba(0,0,0,0.05), rgba(255,255,255,0.05)); -} - -input:focus { - outline: none; - box-shadow: 0 0 0 2px rgba(0,128,240,0.4); - border-radius: 4px; -} - /* Types of text */ -.pre { white-space: pre; overflow: auto; -webkit-overflow-scrolling: touch; } -.msg { display: block; line-height: 1.2em; font-weight: bold; font-size: 1.15em; margin: 4px 8px 8px 8px; white-space: pre-line; } -.request, .finalresults, .sourcever, .invocation, .parameters, .transparent td, .actionfilecontent, .actionslist { font-size: 0.9em; line-height: 1.2em; } /* "Pre sized" */ - -.request, .finalresults, .parameters, .actionfilecontent, .actionslist { /* Dashed outline for excessive text */ - line-height: 1.2em; - padding: 4px; - border: 1px dashed rgba(0,0,0,0.25); - border-right: 0; - background-image: -webkit-linear-gradient(left, #FFF 70%, rgba(255,255,255,0)); - background-image: linear-gradient(to right, #FFF 70%, rgba(255,255,255,0)); - border-radius: 4px 0 0 4px; -} - -.parameters, .finalresults { /* Small scrollbox for excessive text */ - max-height: 116px; - overflow-y: scroll; - -webkit-overflow-scrolling: touch; -} - -.editactions { font-size: 0.8em; } -.actionslist { margin: 4px 0; } - -/* Matching actions in show-url-info */ -.transparent { width: 100%; margin: 0; } -.transparent th { display: block; box-sizing: border-box; text-align: left; padding: 2px 4px; border-radius: 4px; background-image: -webkit-linear-gradient(left, rgba(0,0,0,0.08), transparent 80%); background-image: linear-gradient(to right, rgba(0,0,0,0.08), transparent 80%); } /* Rgba is shared with h2 */ -.transparent td { margin: 4px; display: block; } -.transparent th a { display: none; position: relative; top: -1px; font-size: 0.75em; padding: 2px 5px 1px 3px; } /* Buttons 0.75 0.86 */ -.transparent tr:first-child a, .transparent tr:nth-last-child(2) a { display: inline-block; } /* Only show buttons for First/Last files */ +.msg { white-space: pre; overflow: auto; -webkit-overflow-scrolling: touch; display: block; line-height: 1.2em; font-weight: bold; font-size: 1.15em; margin: 4px 8px 8px 8px; white-space: pre-line; } footer { font-size: 0.8em; text-align: center; width: 87%; margin: 4px auto; } - -/* Specific Fixes */ -.alist br:first-child { display: none; } /* Final results is displayed in "pre format", so remove first BR */ -.filter a[href*="show-status"] { margin-left: 12px; } /* Margin for View/Edit on show-status */ - -/* Safari & iOS */ -@media screen and (min-color-index:0) and(-webkit-min-device-pixel-ratio:0) { @media { - ::-webkit-input-placeholder { position: relative; top: 3px; } - .transparent th a { padding: 2px 4px 1px 4px; } - a.safe { background-color: rgba(0,220,0,0.4); } - a.warn { background-color: rgba(220,0,0,0.4); } -}} - -@media only screen and (orientation:portrait) { - main { width: 100%; border-radius: 0; margin: 0; border-style: none; border-bottom-style: solid; } - header h1 { display: block; overflow: auto; -webkit-overflow-scrolling: touch; } - ::-webkit-input-placeholder { top: 2px; } -} - -@media only screen and (min-device-width : 320px) and (max-device-width : 667px) and (orientation : landscape) { - main { width: 100%; border-radius: 0; margin: 0; border-style: none; border-bottom-style: solid; } - h2 { padding-bottom: 2px; } /* Necessary for underline to show */ - main { font-size: 0.6em; } - ul li { font-size: 1.7em; } - ul li a { font-size: 0.6em; } - input[type='url'] { font-size: 1.85em; } - .buttons { font-size: 1.7em; } - .editactions { font-size: 1.3em; } - .request { font-size: 1.5em; } - .transparent th a { font-size: 1.35em; } - ::-webkit-input-placeholder { top: 2px; } -} diff --git a/advanced/index.php b/advanced/index.php index c51fd71c..7df55d78 100644 --- a/advanced/index.php +++ b/advanced/index.php @@ -68,8 +68,8 @@ if($uri == "/") This page is blocked because it is explicitly contained within the following block list(s): - -
Back to safety +
+
Go back
Generated by Pi-hole
From ca9321624c5187d23c3fe229d70cfb9e696e3663 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 5 Dec 2016 14:40:48 +0100 Subject: [PATCH 11/89] Added padding for ad list results --- advanced/blockingpage.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/advanced/blockingpage.css b/advanced/blockingpage.css index bb37939d..1fdb5f19 100644 --- a/advanced/blockingpage.css +++ b/advanced/blockingpage.css @@ -15,7 +15,7 @@ divs a { border-bottom: 1px dashed rgba(30,60,90,0.3); } b { font-weight: bold; } i { font-style: italic; } -footer, pre, input[type='url'], input[type='text'], .alist, .transparent td { font-family: monospace; } +footer, pre, td { font-family: monospace; padding-left: 15px; } /*body, header { background: #E1E1E1; }*/ body { From 5374f652dd5e44672593b6d83dd9b7dc7bfa95f2 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 6 Dec 2016 08:43:24 +0100 Subject: [PATCH 12/89] No need to modify lighttpd config file, since we overwrite it anyways --- automated install/basic-install.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 958b2d1d..f2e5a9d8 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -964,9 +964,6 @@ accountForRefactor() { sed -i 's/piholeDNS1/PIHOLE_DNS_1/g' ${setupVars} sed -i 's/piholeDNS2/PIHOLE_DNS_2/g' ${setupVars} - # Account for change in lighttpd config file - sed -i 's:pihole/index.html:pihole/index.php:' ${lighttpdConfig} - } updatePihole() { From 480211033d196e0d6322e69480a71c2a0535e920 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 6 Dec 2016 14:05:02 +0100 Subject: [PATCH 13/89] Detect if blocking page is loaded within frame or iframe. If so, hide everything --- advanced/index.php | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/advanced/index.php b/advanced/index.php index 7df55d78..b9c1f7a9 100644 --- a/advanced/index.php +++ b/advanced/index.php @@ -56,7 +56,7 @@ if($uri == "/") - +

Website Blocked

@@ -75,7 +75,25 @@ if($uri == "/") From 75deb0296178ed7dfc7c707938815b3a476b23af Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 6 Dec 2016 14:05:48 +0100 Subject: [PATCH 14/89] Don't query pihole if page is hidden anyhow --- advanced/index.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/advanced/index.php b/advanced/index.php index b9c1f7a9..6d48d723 100644 --- a/advanced/index.php +++ b/advanced/index.php @@ -91,8 +91,10 @@ if($uri == "/") // set white background document.body.style.backgroundImage = "none"; } - - $( "#btnSearch" ).click(); + else + { + $( "#btnSearch" ).click(); + } // } From 505c9e8979d95abb0cad1fddda500997f3379121 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 6 Dec 2016 14:08:45 +0100 Subject: [PATCH 15/89] Minor change --- advanced/index.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/advanced/index.php b/advanced/index.php index 6d48d723..9c7f938b 100644 --- a/advanced/index.php +++ b/advanced/index.php @@ -86,13 +86,15 @@ if($uri == "/") // Try to detect if page is loaded within iframe if(inIframe()) { - // Within iframe -> hide page + // Within iframe + // hide content of page $('#body').hide(); - // set white background + // remove background document.body.style.backgroundImage = "none"; } else { + // Query adlists $( "#btnSearch" ).click(); } // } From 3c4a9bba78d4ae078feb6b0ae5e89589c5704250 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 15 Dec 2016 10:38:24 +0100 Subject: [PATCH 16/89] Minor change --- advanced/index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/advanced/index.php b/advanced/index.php index 9c7f938b..46073867 100644 --- a/advanced/index.php +++ b/advanced/index.php @@ -63,7 +63,7 @@ if($uri == "/")
Access to the following site has been blocked:
-
If you have an ongoing use for this website, please ask the owner of the Pi-Hole in your network to have it whitelisted.
+
If you have an ongoing use for this website, please ask the owner of the Pi-hole in your network to have it whitelisted.
From afabb5957b5265385466eb960390c99c33956b27 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 15 Dec 2016 18:55:40 +0100 Subject: [PATCH 17/89] Implement backend for https://github.com/pi-hole/AdminLTE/pull/253 --- advanced/Scripts/webpage.sh | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/advanced/Scripts/webpage.sh b/advanced/Scripts/webpage.sh index d07e5c94..3e686b93 100755 --- a/advanced/Scripts/webpage.sh +++ b/advanced/Scripts/webpage.sh @@ -212,6 +212,20 @@ SetDNSDomainName(){ } +ResolutionSettings() { + + typ=${args[2]} + state=${args[3]} + + if [[ "${typ}" == "forward" ]]; then + sed -i.bak '/API_GET_UPSTREAM_DNS_HOSTNAME/d;' /etc/pihole/setupVars.conf + echo "API_GET_UPSTREAM_DNS_HOSTNAME=${state}" >> /etc/pihole/setupVars.conf + elif [[ "${typ}" == "clients" ]]; then + sed -i.bak '/API_GET_CLIENT_HOSTNAME/d;' /etc/pihole/setupVars.conf + echo "API_GET_CLIENT_HOSTNAME=${state}" >> /etc/pihole/setupVars.conf + fi +} + case "${args[1]}" in "-p" | "password" ) SetWebPassword;; "-c" | "celsius" ) unit="C"; SetTemperatureUnit;; @@ -227,6 +241,7 @@ case "${args[1]}" in "layout" ) SetWebUILayout;; "-h" | "--help" ) helpFunc;; "domainname" ) SetDNSDomainName;; + "resolve" ) ResolutionSettings;; * ) helpFunc;; esac From 8cb01cdd294584372a94cebb20ebfd6fea0adf0f Mon Sep 17 00:00:00 2001 From: Dan Schaper Date: Fri, 16 Dec 2016 15:08:30 -0800 Subject: [PATCH 18/89] One step to rename variables. Signed-off-by: Dan Schaper --- automated install/basic-install.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 8abf9af8..a04cac3f 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -915,13 +915,12 @@ accountForRefactor() { # At some point in the future this list can be pruned, for now we'll need it to ensure updates don't break. # Refactoring of install script has changed the name of a couple of variables. Sort them out here. - sed -i 's/IPv4addr/IPv4_address/g' ${setupVars} - sed -i 's/piholeIPv6/IPv6_address/g' ${setupVars} - # Account for renaming of global variables. sed -i 's/piholeInterface/PIHOLE_INTERFACE/g' ${setupVars} sed -i 's/IPv4_address/IPV4_ADDRESS/g' ${setupVars} + sed -i 's/IPv4addr/IPV4_ADDRESS/g' ${setupVars} sed -i 's/IPv6_address/IPV6_ADDRESS/g' ${setupVars} + sed -i 's/piholeIPv6/IPV6_ADDRESS/g' ${setupVars} sed -i 's/piholeDNS1/PIHOLE_DNS_1/g' ${setupVars} sed -i 's/piholeDNS2/PIHOLE_DNS_2/g' ${setupVars} From efaee2b68bbb81987016b2a1d854f823ac346e00 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sat, 17 Dec 2016 23:32:50 +0100 Subject: [PATCH 19/89] Improvements for gravity (errors shouldn't be able to escape the black hole if even light cannot do this!) --- gravity.sh | 57 +++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 13 deletions(-) diff --git a/gravity.sh b/gravity.sh index 58d0eef9..e81aaa87 100755 --- a/gravity.sh +++ b/gravity.sh @@ -104,16 +104,26 @@ gravity_collapse() { # patternCheck - check to see if curl downloaded any new files. gravity_patternCheck() { patternBuffer=$1 + success=$2 # check if the patternbuffer is a non-zero length file - if [[ -s "${patternBuffer}" ]]; then - # Some of the blocklists are copyright, they need to be downloaded - # and stored as is. They can be processed for content after they - # have been saved. - mv "${patternBuffer}" "${saveLocation}" - echo " List updated, transport successful!" + if [ $success = true ]; then + if [[ -s "${patternBuffer}" ]]; then + # Some of the blocklists are copyright, they need to be downloaded + # and stored as is. They can be processed for content after they + # have been saved. + mv "${patternBuffer}" "${saveLocation}" + echo "::: List updated, transport successful!" + else + # Empty file -> use previously downloaded list + echo "::: Received empty file, using cached one (list not updated!)" + fi else - # curl didn't download any host files, probably because of the date check - echo " No changes detected, transport skipped!" + # check if cached list exists + if [[ -r "${saveLocation}" ]]; then + echo "::: List download failed, using cached list (list not updated!)" + else + echo "::: Download failed and no cached list available (list will not be considered)" + fi fi } @@ -132,9 +142,27 @@ gravity_transport() { fi # Silently curl url - curl -s -L ${cmd_ext} ${heisenbergCompensator} -A "${agent}" ${url} > ${patternBuffer} - # Check for list updates - gravity_patternCheck "${patternBuffer}" + err=$(curl -s -L ${cmd_ext} ${heisenbergCompensator} -w %{http_code} -A "${agent}" ${url} -o ${patternBuffer}) + + echo " done" + # Analyze http response + echo -n "::: Status: " + case "$err" in + "200" ) echo "Success (OK)"; success=true;; + "304" ) echo "Not modified"; success=false;; + "403" ) echo "Forbidden"; success=false;; + "404" ) echo "Not found"; success=false;; + "408" ) echo "Time-out"; success=false;; + "451" ) echo "Unavailable For Legal Reasons"; success=false;; + "521" ) echo "Web Server Is Down (Cloudflare)"; success=false;; + "522" ) echo "Connection Timed Out (Cloudflare)"; success=false;; + "500" ) echo "Internal Server Error"; success=false;; + * ) echo "Status $err"; success=false;; + esac + + # Process result + gravity_patternCheck "${patternBuffer}" ${success} + } # spinup - main gravity function @@ -181,7 +209,10 @@ gravity_Schwarzchild() { echo -n "::: Aggregating list of domains..." truncate -s 0 ${piholeDir}/${matterAndLight} for i in "${activeDomains[@]}"; do - cat "${i}" | tr -d '\r' >> ${piholeDir}/${matterAndLight} + # Only assimilate list if it is available (download might have faild permanently) + if [[ -r "${i}" ]]; then + cat "${i}" | tr -d '\r' >> ${piholeDir}/${matterAndLight} + fi done echo " done!" } @@ -353,7 +384,7 @@ if [[ "${forceGrav}" == true ]]; then fi #Overwrite adlists.default from /etc/.pihole in case any changes have been made. Changes should be saved in /etc/adlists.list -cp /etc/.pihole/adlists.default /etc/pihole/adlists.default +#cp /etc/.pihole/adlists.default /etc/pihole/adlists.default gravity_collapse gravity_spinup if [[ "${skipDownload}" == false ]]; then From cd0c3f941804ea7f44912b8be2e8341b4a3ae2f2 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sat, 17 Dec 2016 23:46:28 +0100 Subject: [PATCH 20/89] Better output if file has not been downloaded because it has not been changed --- gravity.sh | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/gravity.sh b/gravity.sh index e81aaa87..b5a3765c 100755 --- a/gravity.sh +++ b/gravity.sh @@ -105,9 +105,13 @@ gravity_collapse() { gravity_patternCheck() { patternBuffer=$1 success=$2 - # check if the patternbuffer is a non-zero length file + error=$3 if [ $success = true ]; then - if [[ -s "${patternBuffer}" ]]; then + # check if download was successful but list has not been modified + if [ "${error}" == "304" ]; then + echo "::: No changes detected, transport skipped!" + # check if the patternbuffer is a non-zero length file + elif [[ -s "${patternBuffer}" ]]; then # Some of the blocklists are copyright, they need to be downloaded # and stored as is. They can be processed for content after they # have been saved. @@ -148,20 +152,20 @@ gravity_transport() { # Analyze http response echo -n "::: Status: " case "$err" in - "200" ) echo "Success (OK)"; success=true;; - "304" ) echo "Not modified"; success=false;; - "403" ) echo "Forbidden"; success=false;; - "404" ) echo "Not found"; success=false;; - "408" ) echo "Time-out"; success=false;; - "451" ) echo "Unavailable For Legal Reasons"; success=false;; - "521" ) echo "Web Server Is Down (Cloudflare)"; success=false;; - "522" ) echo "Connection Timed Out (Cloudflare)"; success=false;; - "500" ) echo "Internal Server Error"; success=false;; - * ) echo "Status $err"; success=false;; + "200" ) echo "Success (OK)"; success=true;; + "304" ) echo "Not modified"; success=true;; + "403" ) echo "Forbidden"; success=false;; + "404" ) echo "Not found"; success=false;; + "408" ) echo "Time-out"; success=false;; + "451" ) echo "Unavailable For Legal Reasons"; success=false;; + "521" ) echo "Web Server Is Down (Cloudflare)"; success=false;; + "522" ) echo "Connection Timed Out (Cloudflare)"; success=false;; + "500" ) echo "Internal Server Error"; success=false;; + * ) echo "Status $err"; success=false;; esac # Process result - gravity_patternCheck "${patternBuffer}" ${success} + gravity_patternCheck "${patternBuffer}" ${success} "${err}" } From 715838cf892f9718b4a9afc94210a872b3c268ef Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 19 Dec 2016 13:42:42 +0100 Subject: [PATCH 21/89] Allow disabling second DNS server --- advanced/Scripts/webpage.sh | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/advanced/Scripts/webpage.sh b/advanced/Scripts/webpage.sh index d07e5c94..6251b158 100755 --- a/advanced/Scripts/webpage.sh +++ b/advanced/Scripts/webpage.sh @@ -75,12 +75,18 @@ SetDNSServers(){ sed -i.bak '/PIHOLE_DNS_1/d;/PIHOLE_DNS_2/d;/DNS_FQDN_REQUIRED/d;' /etc/pihole/setupVars.conf # Save setting to file echo "PIHOLE_DNS_1=${args[2]}" >> /etc/pihole/setupVars.conf - echo "PIHOLE_DNS_2=${args[3]}" >> /etc/pihole/setupVars.conf + if [[ "${args[3]}" != "none" ]]; then + echo "PIHOLE_DNS_2=${args[3]}" >> /etc/pihole/setupVars.conf + else + echo "PIHOLE_DNS_2=" >> /etc/pihole/setupVars.conf + fi # Replace within actual dnsmasq config file sed -i '/server=/d;' /etc/dnsmasq.d/01-pihole.conf echo "server=${args[2]}" >> /etc/dnsmasq.d/01-pihole.conf - echo "server=${args[3]}" >> /etc/dnsmasq.d/01-pihole.conf + if [[ "${args[3]}" != "none" ]]; then + echo "server=${args[3]}" >> /etc/dnsmasq.d/01-pihole.conf + fi # Remove domain-needed entry sed -i '/domain-needed/d;' /etc/dnsmasq.d/01-pihole.conf From da363070c7770f738c18c13120828edf722f149c Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 20 Dec 2016 12:21:08 +0100 Subject: [PATCH 22/89] Added Kelvin --- advanced/Scripts/webpage.sh | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/advanced/Scripts/webpage.sh b/advanced/Scripts/webpage.sh index 6251b158..6b54abb7 100755 --- a/advanced/Scripts/webpage.sh +++ b/advanced/Scripts/webpage.sh @@ -31,11 +31,7 @@ SetTemperatureUnit(){ # Remove setting from file (create backup setupVars.conf.bak) sed -i.bak '/TEMPERATUREUNIT/d' /etc/pihole/setupVars.conf # Save setting to file - if [[ $unit == "F" ]] ; then - echo "TEMPERATUREUNIT=F" >> /etc/pihole/setupVars.conf - else - echo "TEMPERATUREUNIT=C" >> /etc/pihole/setupVars.conf - fi + echo "TEMPERATUREUNIT=${unit}" >> /etc/pihole/setupVars.conf } @@ -222,6 +218,7 @@ case "${args[1]}" in "-p" | "password" ) SetWebPassword;; "-c" | "celsius" ) unit="C"; SetTemperatureUnit;; "-f" | "fahrenheit" ) unit="F"; SetTemperatureUnit;; + "-k" | "kelvin" ) unit="K"; SetTemperatureUnit;; "setdns" ) SetDNSServers;; "setexcludedomains" ) SetExcludeDomains;; "setexcludeclients" ) SetExcludeClients;; From ee8f29d178ef18d68057564af71f4ab1da45a3cc Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 20 Dec 2016 16:28:28 +0100 Subject: [PATCH 23/89] Fixed typo, extended help text --- advanced/Scripts/webpage.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/advanced/Scripts/webpage.sh b/advanced/Scripts/webpage.sh index 6b54abb7..07706346 100755 --- a/advanced/Scripts/webpage.sh +++ b/advanced/Scripts/webpage.sh @@ -19,8 +19,9 @@ helpFunc() { ::: ::: Options: ::: -p, password Set web interface password, an empty input will remove any previously set password -::: -c, celsius Set Celcius temperature unit +::: -c, celsius Set Celsius temperature unit ::: -f, fahrenheit Set Fahrenheit temperature unit +::: -k, kelvin Set Kelvin temperature unit ::: -h, --help Show this help dialog EOM exit 0 From d0e3c546f80c86f2eef82df82bcfab88cd137417 Mon Sep 17 00:00:00 2001 From: Dan Schaper Date: Tue, 20 Dec 2016 15:00:37 -0800 Subject: [PATCH 24/89] CRLF to CR (Win to Unix line endings) Signed-off-by: Dan Schaper --- automated install/basic-install.sh | 1155 +--------------------------- 1 file changed, 1 insertion(+), 1154 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index a04cac3f..96c8ece3 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -1,1154 +1 @@ -#!/usr/bin/env bash -# Pi-hole: A black hole for Internet advertisements -# (c) 2015, 2016 by Jacob Salmela -# Network-wide ad blocking via your Raspberry Pi -# http://pi-hole.net -# Installs Pi-hole -# -# Pi-hole is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. - -# pi-hole.net/donate -# -# Install with this command (from your Pi): -# -# curl -L install.pi-hole.net | bash - -set -e -######## VARIABLES ######### -tmpLog=/tmp/pihole-install.log -instalLogLoc=/etc/pihole/install.log -setupVars=/etc/pihole/setupVars.conf - -webInterfaceGitUrl="https://github.com/pi-hole/AdminLTE.git" -webInterfaceDir="/var/www/html/admin" -piholeGitUrl="https://github.com/pi-hole/pi-hole.git" -PI_HOLE_LOCAL_REPO="/etc/.pihole" -PI_HOLE_FILES=(chronometer list piholeDebug piholeLogFlush setupLCD update version) -useUpdateVars=false - -IPV4_ADDRESS="" -IPV6_ADDRESS="" -QUERY_LOGGING=true - -# Find the rows and columns will default to 80x24 is it can not be detected -screen_size=$(stty size 2>/dev/null || echo 24 80) -rows=$(echo $screen_size | awk '{print $1}') -columns=$(echo $screen_size | awk '{print $2}') - -# Divide by two so the dialogs take up half of the screen, which looks nice. -r=$(( rows / 2 )) -c=$(( columns / 2 )) -# Unless the screen is tiny -r=$(( r < 20 ? 20 : r )) -c=$(( c < 70 ? 70 : c )) - -######## Undocumented Flags. Shhh ######## -skipSpaceCheck=false -reconfigure=false -runUnattended=false - -######## FIRST CHECK ######## -# Must be root to install -echo ":::" -if [[ ${EUID} -eq 0 ]]; then - echo "::: You are root." -else - echo "::: Script called with non-root privileges. The Pi-hole installs server packages and configures" - echo "::: system networking, it requires elevated rights. Please check the contents of the script for" - echo "::: any concerns with this requirement. Please be sure to download this script from a trusted source." - echo ":::" - echo "::: Detecting the presence of the sudo utility for continuation of this install..." - - if [ -x "$(command -v sudo)" ]; then - echo "::: Utility sudo located." - exec curl -sSL https://install.pi-hole.net | sudo bash "$@" - exit $? - else - echo "::: sudo is needed for the Web interface to run pihole commands. Please run this script as root and it will be automatically installed." - exit 1 - fi -fi - -# Compatibility - -if [[ $(command -v apt-get) ]]; then - #Debian Family - ############################################# - PKG_MANAGER="apt-get" - PKG_CACHE="/var/lib/apt/lists/" - UPDATE_PKG_CACHE="${PKG_MANAGER} update" - PKG_UPDATE="${PKG_MANAGER} upgrade" - PKG_INSTALL="${PKG_MANAGER} --yes --fix-missing install" - # grep -c will return 1 retVal on 0 matches, block this throwing the set -e with an OR TRUE - PKG_COUNT="${PKG_MANAGER} -s -o Debug::NoLocking=true upgrade | grep -c ^Inst || true" - # ######################################### - # fixes for dependancy differences - # Debian 7 doesn't have iproute2 use iproute - ${PKG_MANAGER} install --dry-run iproute2 > /dev/null 2>&1 && IPROUTE_PKG="iproute2" || IPROUTE_PKG="iproute" - # Prefer the php metapackage if it's there, fall back on the php5 pacakges - ${PKG_MANAGER} install --dry-run php > /dev/null 2>&1 && phpVer="php" || phpVer="php5" - # ######################################### - INSTALLER_DEPS=( apt-utils whiptail git dhcpcd5) - PIHOLE_DEPS=( iputils-ping lsof dnsutils bc dnsmasq lighttpd ${phpVer}-common ${phpVer}-cgi curl unzip wget sudo netcat cron ${IPROUTE_PKG} ) - LIGHTTPD_USER="www-data" - LIGHTTPD_GROUP="www-data" - LIGHTTPD_CFG="lighttpd.conf.debian" - DNSMASQ_USER="dnsmasq" - - package_check_install() { - dpkg-query -W -f='${Status}' "${1}" 2>/dev/null | grep -c "ok installed" || ${PKG_INSTALL} "${1}" - } -elif [ $(command -v rpm) ]; then - # Fedora Family - if [ $(command -v dnf) ]; then - PKG_MANAGER="dnf" - else - PKG_MANAGER="yum" - fi - PKG_CACHE="/var/cache/${PKG_MANAGER}" - UPDATE_PKG_CACHE="${PKG_MANAGER} check-update" - PKG_UPDATE="${PKG_MANAGER} update -y" - PKG_INSTALL="${PKG_MANAGER} install -y" - PKG_COUNT="${PKG_MANAGER} check-update | egrep '(.i686|.x86|.noarch|.arm|.src)' | wc -l" - INSTALLER_DEPS=( iproute net-tools procps-ng newt git ) - PIHOLE_DEPS=( epel-release bind-utils bc dnsmasq lighttpd lighttpd-fastcgi php-common php-cli php curl unzip wget findutils cronie sudo nmap-ncat ) - - if grep -q 'Fedora' /etc/redhat-release; then - remove_deps=(epel-release); - PIHOLE_DEPS=( ${PIHOLE_DEPS[@]/$remove_deps} ); - fi - LIGHTTPD_USER="lighttpd" - LIGHTTPD_GROUP="lighttpd" - LIGHTTPD_CFG="lighttpd.conf.fedora" - DNSMASQ_USER="nobody" - - package_check_install() { - rpm -qa | grep ^"${1}"- > /dev/null || ${PKG_INSTALL} "${1}" - } -else - echo "OS distribution not supported" - exit -fi - -####### FUNCTIONS ########## -spinner() { - local pid=$1 - local delay=0.50 - local spinstr='/-\|' - - while [ "$(ps a | awk '{print $1}' | grep "${pid}")" ]; do - local temp=${spinstr#?} - printf " [%c] " "${spinstr}" - local spinstr=${temp}${spinstr%"$temp"} - sleep ${delay} - printf "\b\b\b\b\b\b" - done - printf " \b\b\b\b" -} - -is_repo() { - # Use git to check if directory is currently under VCS, return the value - local directory="${1}" - git -C "${directory}" status --short &> /dev/null - return -} - -make_repo() { - local directory="${1}" - local remoteRepo="${2}" - # Remove the non-repod interface and clone the interface - echo -n "::: Cloning $remoteRepo into $directory..." - rm -rf "${directory}" - git clone -q --depth 1 "${remoteRepo}" "${directory}" > /dev/null & spinner $! - echo " done!" -} - -update_repo() { - local directory="${1}" - # Pull the latest commits - echo -n "::: Updating repo in $1..." - cd "${directory}" || exit 1 - git stash -q > /dev/null & spinner $! - git pull -q > /dev/null & spinner $! - echo " done!" -} - -getGitFiles() { - # Setup git repos for directory and repository passed - # as arguments 1 and 2 - local directory="${1}" - local remoteRepo="${2}" - echo ":::" - echo "::: Checking for existing repository..." - if is_repo "${directory}"; then - update_repo "${directory}" - else - make_repo "${directory}" "${remoteRepo}" - fi -} - -find_IPv4_information() { - # Find IP used to route to outside world - IPv4dev=$(ip route get 8.8.8.8 | awk '{for(i=1;i<=NF;i++)if($i~/dev/)print $(i+1)}') - IPV4_ADDRESS=$(ip -o -f inet addr show dev "$IPv4dev" | awk '{print $4}' | awk 'END {print}') - IPv4gw=$(ip route get 8.8.8.8 | awk '{print $3}') -} - -get_available_interfaces() { - # Get available interfaces. Consider only getting UP interfaces in the future, and leaving DOWN interfaces out of list. - availableInterfaces=$(ip -o link | awk '{print $2}' | grep -v "lo" | cut -d':' -f1 | cut -d'@' -f1) -} - -welcomeDialogs() { - # Display the welcome dialog - whiptail --msgbox --backtitle "Welcome" --title "Pi-hole automated installer" "\n\nThis installer will transform your device into a network-wide ad blocker!" ${r} ${c} - - # Support for a part-time dev - whiptail --msgbox --backtitle "Plea" --title "Free and open source" "\n\nThe Pi-hole is free, but powered by your donations: http://pi-hole.net/donate" ${r} ${c} - - # Explain the need for a static address - whiptail --msgbox --backtitle "Initiating network interface" --title "Static IP Needed" "\n\nThe Pi-hole is a SERVER so it needs a STATIC IP ADDRESS to function properly. - -In the next section, you can choose to use your current network settings (DHCP) or to manually edit them." ${r} ${c} -} - -verifyFreeDiskSpace() { - - # 50MB is the minimum space needed (45MB install (includes web admin bootstrap/jquery libraries etc) + 5MB one day of logs.) - # - Fourdee: Local ensures the variable is only created, and accessible within this function/void. Generally considered a "good" coding practice for non-global variables. - echo "::: Verifying free disk space..." - local required_free_kilobytes=51200 - local existing_free_kilobytes=$(df -Pk | grep -m1 '\/$' | awk '{print $4}') - - # - Unknown free disk space , not a integer - if ! [[ "${existing_free_kilobytes}" =~ ^([0-9])+$ ]]; then - echo "::: Unknown free disk space!" - echo "::: We were unable to determine available free disk space on this system." - echo "::: You may override this check and force the installation, however, it is not recommended" - echo "::: To do so, pass the argument '--i_do_not_follow_recommendations' to the install script" - echo "::: eg. curl -L https://install.pi-hole.net | bash /dev/stdin --i_do_not_follow_recommendations" - exit 1 - # - Insufficient free disk space - elif [[ ${existing_free_kilobytes} -lt ${required_free_kilobytes} ]]; then - echo "::: Insufficient Disk Space!" - echo "::: Your system appears to be low on disk space. pi-hole recommends a minimum of $required_free_kilobytes KiloBytes." - echo "::: You only have ${existing_free_kilobytes} KiloBytes free." - echo "::: If this is a new install you may need to expand your disk." - echo "::: Try running 'sudo raspi-config', and choose the 'expand file system option'" - echo "::: After rebooting, run this installation again. (curl -L https://install.pi-hole.net | bash)" - - echo "Insufficient free space, exiting..." - exit 1 - - fi - -} - - -chooseInterface() { - # Turn the available interfaces into an array so it can be used with a whiptail dialog - local interfacesArray=() - # Number of available interfaces - local interfaceCount - # Whiptail variable storage - local chooseInterfaceCmd - # Temporary Whiptail options storage - local chooseInterfaceOptions - # Loop sentinel variable - local firstLoop=1 - - while read -r line; do - mode="OFF" - if [[ ${firstLoop} -eq 1 ]]; then - firstLoop=0 - mode="ON" - fi - interfacesArray+=("${line}" "available" "${mode}") - done <<< "${availableInterfaces}" - - # Find out how many interfaces are available to choose from - interfaceCount=$(echo "${availableInterfaces}" | wc -l) - chooseInterfaceCmd=(whiptail --separate-output --radiolist "Choose An Interface (press space to select)" ${r} ${c} ${interfaceCount}) - chooseInterfaceOptions=$("${chooseInterfaceCmd[@]}" "${interfacesArray[@]}" 2>&1 >/dev/tty) - if [[ $? = 0 ]]; then - for desiredInterface in ${chooseInterfaceOptions}; do - PIHOLE_INTERFACE=${desiredInterface} - echo "::: Using interface: $PIHOLE_INTERFACE" - done - else - echo "::: Cancel selected, exiting...." - exit 1 - fi -} - -useIPv6dialog() { - # Show the IPv6 address used for blocking - IPV6_ADDRESS=$(ip -6 route get 2001:4860:4860::8888 | awk -F " " '{ for(i=1;i<=NF;i++) if ($i == "src") print $(i+1) }') - whiptail --msgbox --backtitle "IPv6..." --title "IPv6 Supported" "$IPV6_ADDRESS will be used to block ads." ${r} ${c} -} - - -use4andor6() { - local useIPv4 - local useIPv6 - # Let use select IPv4 and/or IPv6 - cmd=(whiptail --separate-output --checklist "Select Protocols (press space to select)" ${r} ${c} 2) - options=(IPv4 "Block ads over IPv4" on - IPv6 "Block ads over IPv6" on) - choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty) - if [[ $? = 0 ]];then - for choice in ${choices} - do - case ${choice} in - IPv4 ) useIPv4=true;; - IPv6 ) useIPv6=true;; - esac - done - if [[ ${useIPv4} ]]; then - find_IPv4_information - getStaticIPv4Settings - setStaticIPv4 - fi - if [[ ${useIPv6} ]]; then - useIPv6dialog - fi - echo "::: IPv4 address: ${IPV4_ADDRESS}" - echo "::: IPv6 address: ${IPV6_ADDRESS}" - if [ ! ${useIPv4} ] && [ ! ${useIPv6} ]; then - echo "::: Cannot continue, neither IPv4 or IPv6 selected" - echo "::: Exiting" - exit 1 - fi - else - echo "::: Cancel selected. Exiting..." - exit 1 - fi -} - -getStaticIPv4Settings() { - # Ask if the user wants to use DHCP settings as their static IP - if (whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Do you want to use your current network settings as a static address? - IP address: ${IPV4_ADDRESS} - Gateway: ${IPv4gw}" ${r} ${c}); then - # If they choose yes, let the user know that the IP address will not be available via DHCP and may cause a conflict. - whiptail --msgbox --backtitle "IP information" --title "FYI: IP Conflict" "It is possible your router could still try to assign this IP to a device, which would cause a conflict. But in most cases the router is smart enough to not do that. -If you are worried, either manually set the address, or modify the DHCP reservation pool so it does not include the IP you want. -It is also possible to use a DHCP reservation, but if you are going to do that, you might as well set a static address." ${r} ${c} - # Nothing else to do since the variables are already set above - else - # Otherwise, we need to ask the user to input their desired settings. - # Start by getting the IPv4 address (pre-filling it with info gathered from DHCP) - # Start a loop to let the user enter their information with the chance to go back and edit it if necessary - until [[ ${ipSettingsCorrect} = True ]]; do - # Ask for the IPv4 address - IPV4_ADDRESS=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 address" --inputbox "Enter your desired IPv4 address" ${r} ${c} "${IPV4_ADDRESS}" 3>&1 1>&2 2>&3) - if [[ $? = 0 ]]; then - echo "::: Your static IPv4 address: ${IPV4_ADDRESS}" - # Ask for the gateway - IPv4gw=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 gateway (router)" --inputbox "Enter your desired IPv4 default gateway" ${r} ${c} "${IPv4gw}" 3>&1 1>&2 2>&3) - if [[ $? = 0 ]]; then - echo "::: Your static IPv4 gateway: ${IPv4gw}" - # Give the user a chance to review their settings before moving on - if (whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Are these settings correct? - IP address: ${IPV4_ADDRESS} - Gateway: ${IPv4gw}" ${r} ${c}); then - # After that's done, the loop ends and we move on - ipSettingsCorrect=True - else - # If the settings are wrong, the loop continues - ipSettingsCorrect=False - fi - else - # Cancelling gateway settings window - ipSettingsCorrect=False - echo "::: Cancel selected. Exiting..." - exit 1 - fi - else - # Cancelling IPv4 settings window - ipSettingsCorrect=False - echo "::: Cancel selected. Exiting..." - exit 1 - fi - done - # End the if statement for DHCP vs. static - fi -} - -setDHCPCD() { - # Append these lines to dhcpcd.conf to enable a static IP - echo "## interface ${PIHOLE_INTERFACE} - static ip_address=${IPV4_ADDRESS} - static routers=${IPv4gw} - static domain_name_servers=${IPv4gw}" | tee -a /etc/dhcpcd.conf >/dev/null -} - -setStaticIPv4() { - local IFCFG_FILE - local IPADDR - local CIDR - if [[ -f /etc/dhcpcd.conf ]]; then - # Debian Family - if grep -q "${IPV4_ADDRESS}" /etc/dhcpcd.conf; then - echo "::: Static IP already configured" - else - setDHCPCD - ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" - echo ":::" - echo "::: Setting IP to ${IPV4_ADDRESS}. You may need to restart after the install is complete." - echo ":::" - fi - elif [[ -f /etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE} ]];then - # Fedora Family - IFCFG_FILE=/etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE} - if grep -q "${IPV4_ADDRESS}" "${IFCFG_FILE}"; then - echo "::: Static IP already configured" - else - IPADDR=$(echo "${IPV4_ADDRESS}" | cut -f1 -d/) - CIDR=$(echo "${IPV4_ADDRESS}" | cut -f2 -d/) - # Backup existing interface configuration: - cp "${IFCFG_FILE}" "${IFCFG_FILE}".pihole.orig - # Build Interface configuration file: - { - echo "# Configured via Pi-Hole installer" - echo "DEVICE=$PIHOLE_INTERFACE" - echo "BOOTPROTO=none" - echo "ONBOOT=yes" - echo "IPADDR=$IPADDR" - echo "PREFIX=$CIDR" - echo "GATEWAY=$IPv4gw" - echo "DNS1=$PIHOLE_DNS_1" - echo "DNS2=$PIHOLE_DNS_2" - echo "USERCTL=no" - }> "${IFCFG_FILE}" - ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" - if [ -x "$(command -v nmcli)" ];then - # Tell NetworkManager to read our new sysconfig file - nmcli con load "${IFCFG_FILE}" > /dev/null - fi - echo ":::" - echo "::: Setting IP to ${IPV4_ADDRESS}. You may need to restart after the install is complete." - echo ":::" - fi - else - echo "::: Warning: Unable to locate configuration file to set static IPv4 address!" - exit 1 - fi -} - -valid_ip() { - local ip=${1} - local stat=1 - - if [[ ${ip} =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then - OIFS=$IFS - IFS='.' - ip=(${ip}) - IFS=${OIFS} - [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \ - && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]] - stat=$? - fi - return ${stat} -} - -setDNS() { - DNSChooseCmd=(whiptail --separate-output --radiolist "Select Upstream DNS Provider. To use your own, select Custom." ${r} ${c} 6) - DNSChooseOptions=(Google "" on - OpenDNS "" off - Level3 "" off - Norton "" off - Comodo "" off - Custom "" off) - DNSchoices=$("${DNSChooseCmd[@]}" "${DNSChooseOptions[@]}" 2>&1 >/dev/tty) - if [[ $? = 0 ]];then - case ${DNSchoices} in - Google) - echo "::: Using Google DNS servers." - PIHOLE_DNS_1="8.8.8.8" - PIHOLE_DNS_2="8.8.4.4" - ;; - OpenDNS) - echo "::: Using OpenDNS servers." - PIHOLE_DNS_1="208.67.222.222" - PIHOLE_DNS_2="208.67.220.220" - ;; - Level3) - echo "::: Using Level3 servers." - PIHOLE_DNS_1="4.2.2.1" - PIHOLE_DNS_2="4.2.2.2" - ;; - Norton) - echo "::: Using Norton ConnectSafe servers." - PIHOLE_DNS_1="199.85.126.10" - PIHOLE_DNS_2="199.85.127.10" - ;; - Comodo) - echo "::: Using Comodo Secure servers." - PIHOLE_DNS_1="8.26.56.26" - PIHOLE_DNS_2="8.20.247.20" - ;; - Custom) - until [[ ${DNSSettingsCorrect} = True ]]; do - strInvalid="Invalid" - if [ ! ${PIHOLE_DNS_1} ]; then - if [ ! ${PIHOLE_DNS_2} ]; then - prePopulate="" - else - prePopulate=", ${PIHOLE_DNS_2}" - fi - elif [ ${PIHOLE_DNS_1} ] && [ ! ${PIHOLE_DNS_2} ]; then - prePopulate="${PIHOLE_DNS_1}" - elif [ ${PIHOLE_DNS_1} ] && [ ${PIHOLE_DNS_2} ]; then - prePopulate="${PIHOLE_DNS_1}, ${PIHOLE_DNS_2}" - fi - - piholeDNS=$(whiptail --backtitle "Specify Upstream DNS Provider(s)" --inputbox "Enter your desired upstream DNS provider(s), seperated by a comma.\n\nFor example '8.8.8.8, 8.8.4.4'" ${r} ${c} "${prePopulate}" 3>&1 1>&2 2>&3) - - if [[ $? = 0 ]]; then - PIHOLE_DNS_1=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$1}') - PIHOLE_DNS_2=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$2}') - if ! valid_ip "${PIHOLE_DNS_1}" || [ ! "${PIHOLE_DNS_1}" ]; then - PIHOLE_DNS_1=${strInvalid} - fi - if ! valid_ip "${PIHOLE_DNS_2}" && [ "${PIHOLE_DNS_2}" ]; then - PIHOLE_DNS_2=${strInvalid} - fi - else - echo "::: Cancel selected, exiting...." - exit 1 - fi - if [[ ${PIHOLE_DNS_1} == "${strInvalid}" ]] || [[ ${PIHOLE_DNS_2} == "${strInvalid}" ]]; then - whiptail --msgbox --backtitle "Invalid IP" --title "Invalid IP" "One or both entered IP addresses were invalid. Please try again.\n\n DNS Server 1: $PIHOLE_DNS_1\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c} - if [[ ${PIHOLE_DNS_1} == "${strInvalid}" ]]; then - PIHOLE_DNS_1="" - fi - if [[ ${PIHOLE_DNS_2} == "${strInvalid}" ]]; then - PIHOLE_DNS_2="" - fi - DNSSettingsCorrect=False - else - if (whiptail --backtitle "Specify Upstream DNS Provider(s)" --title "Upstream DNS Provider(s)" --yesno "Are these settings correct?\n DNS Server 1: $PIHOLE_DNS_1\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c}); then - DNSSettingsCorrect=True - else - # If the settings are wrong, the loop continues - DNSSettingsCorrect=False - fi - fi - done - ;; - esac - else - echo "::: Cancel selected. Exiting..." - exit 1 - fi -} - -setLogging() { - local LogToggleCommand - local LogChooseOptions - local LogChoices - - LogToggleCommand=(whiptail --separate-output --radiolist "Do you want to log queries?\n (Disabling will render graphs on the Admin page useless):" ${r} ${c} 6) - LogChooseOptions=("On (Reccomended)" "" on - Off "" off) - LogChoices=$("${LogToggleCommand[@]}" "${LogChooseOptions[@]}" 2>&1 >/dev/tty) || (echo "::: Cancel selected. Exiting..." && exit 1) - case ${LogChoices} in - "On (Recommended)") - echo "::: Logging On." - QUERY_LOGGING=true - ;; - Off) - echo "::: Logging Off." - QUERY_LOGGING=false - ;; - esac -} - - -version_check_dnsmasq() { - # Check if /etc/dnsmasq.conf is from pihole. If so replace with an original and install new in .d directory - local dnsmasq_conf="/etc/dnsmasq.conf" - local dnsmasq_conf_orig="/etc/dnsmasq.conf.orig" - local dnsmasq_pihole_id_string="addn-hosts=/etc/pihole/gravity.list" - local dnsmasq_original_config="/etc/.pihole/advanced/dnsmasq.conf.original" - local dnsmasq_pihole_01_snippet="/etc/.pihole/advanced/01-pihole.conf" - local dnsmasq_pihole_01_location="/etc/dnsmasq.d/01-pihole.conf" - - if [ -f ${dnsmasq_conf} ]; then - echo -n "::: Existing dnsmasq.conf found..." - if grep -q ${dnsmasq_pihole_id_string} ${dnsmasq_conf}; then - echo " it is from a previous pi-hole install." - echo -n "::: Backing up dnsmasq.conf to dnsmasq.conf.orig..." - mv -f ${dnsmasq_conf} ${dnsmasq_conf_orig} - echo " done." - echo -n "::: Restoring default dnsmasq.conf..." - cp ${dnsmasq_original_config} ${dnsmasq_conf} - echo " done." - else - echo " it is not a pi-hole file, leaving alone!" - fi - else - echo -n "::: No dnsmasq.conf found.. restoring default dnsmasq.conf..." - cp ${dnsmasq_original_config} ${dnsmasq_conf} - echo " done." - fi - - echo -n "::: Copying 01-pihole.conf to /etc/dnsmasq.d/01-pihole.conf..." - cp ${dnsmasq_pihole_01_snippet} ${dnsmasq_pihole_01_location} - echo " done." - sed -i "s/@INT@/$PIHOLE_INTERFACE/" ${dnsmasq_pihole_01_location} - if [[ "${PIHOLE_DNS_1}" != "" ]]; then - sed -i "s/@DNS1@/$PIHOLE_DNS_1/" ${dnsmasq_pihole_01_location} - else - sed -i '/^server=@DNS1@/d' ${dnsmasq_pihole_01_location} - fi - if [[ "${PIHOLE_DNS_2}" != "" ]]; then - sed -i "s/@DNS2@/$PIHOLE_DNS_2/" ${dnsmasq_pihole_01_location} - else - sed -i '/^server=@DNS2@/d' ${dnsmasq_pihole_01_location} - fi - - sed -i 's/^#conf-dir=\/etc\/dnsmasq.d$/conf-dir=\/etc\/dnsmasq.d/' ${dnsmasq_conf} - - if [[ "${QUERY_LOGGING}" == false ]] ; then - #Disable Logging - sed -i 's/^log-queries/#log-queries/' ${dnsmasq_pihole_01_location} - else - #Enable Logging - sed -i 's/^#log-queries/log-queries/' ${dnsmasq_pihole_01_location} - fi -} - -remove_legacy_scripts() { - #Tidy up /usr/local/bin directory if installing over previous install. - oldFiles=( gravity chronometer whitelist blacklist piholeLogFlush updateDashboard uninstall setupLCD piholeDebug) - for i in "${oldFiles[@]}"; do - if [ -f "/usr/local/bin/$i.sh" ]; then - rm /usr/local/bin/"$i".sh - fi - done -} - -clean_existing() { - # Clean an exiting installation to prepare for upgrade/reinstall - # ${1} Directory to clean; ${2} Array of files to remove - local clean_directory="${1}" - local old_files=${2} - - for script in "${old_files[@]}"; do - rm -f "${clean_directory}${script}.sh" - done - -} - -installScripts() { - # Install the scripts from repository to their various locations - readonly install_dir="/opt/pihole/" - - echo ":::" - echo -n "::: Installing scripts from ${PI_HOLE_LOCAL_REPO}..." - - # Clear out script files from Pi-hole scripts directory. - clean_existing "${install_dir}" "${PI_HOLE_FILES}" - - # Install files from local core repository - if is_repo "${PI_HOLE_LOCAL_REPO}"; then - cd "${PI_HOLE_LOCAL_REPO}" - install -o "${USER}" -Dm755 -t /opt/pihole/ gravity.sh - install -o "${USER}" -Dm755 -t /opt/pihole/ ./advanced/Scripts/*.sh - install -o "${USER}" -Dm755 -t /opt/pihole/ ./automated\ install/uninstall.sh - install -o "${USER}" -Dm755 -t /usr/local/bin/ pihole - install -Dm644 ./advanced/bash-completion/pihole /etc/bash_completion.d/pihole - echo " done." - else - echo " *** ERROR: Local repo ${core_repo} not found, exiting." - exit 1 - fi -} - -installConfigs() { - # Install the configs from /etc/.pihole to their various locations - echo ":::" - echo "::: Installing configs..." - version_check_dnsmasq - if [ ! -d "/etc/lighttpd" ]; then - mkdir /etc/lighttpd - chown "${USER}":root /etc/lighttpd - elif [ -f "/etc/lighttpd/lighttpd.conf" ]; then - mv /etc/lighttpd/lighttpd.conf /etc/lighttpd/lighttpd.conf.orig - fi - cp /etc/.pihole/advanced/${LIGHTTPD_CFG} /etc/lighttpd/lighttpd.conf - mkdir -p /var/run/lighttpd - chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/run/lighttpd - mkdir -p /var/cache/lighttpd/compress - chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/compress - mkdir -p /var/cache/lighttpd/uploads - chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/uploads -} - -stop_service() { - # Stop service passed in as argument. - # Can softfail, as process may not be installed when this is called - echo ":::" - echo -n "::: Stopping ${1} service..." - if [ -x "$(command -v systemctl)" ]; then - systemctl stop "${1}" &> /dev/null & spinner $! || true - else - service "${1}" stop &> /dev/null & spinner $! || true - fi - echo " done." -} - -start_service() { - # Start/Restart service passed in as argument - # This should not fail, it's an error if it does - echo ":::" - echo -n "::: Starting ${1} service..." - if [ -x "$(command -v systemctl)" ]; then - systemctl restart "${1}" &> /dev/null & spinner $! - else - service "${1}" restart &> /dev/null & spinner $! - fi - echo " done." -} - -enable_service() { - # Enable service so that it will start with next reboot - echo ":::" - echo -n "::: Enabling ${1} service to start on reboot..." - if [ -x "$(command -v systemctl)" ]; then - systemctl enable "${1}" &> /dev/null & spinner $! - else - update-rc.d "${1}" defaults &> /dev/null & spinner $! - fi - echo " done." -} - -update_pacakge_cache() { - #Running apt-get update/upgrade with minimal output can cause some issues with - #requiring user input (e.g password for phpmyadmin see #218) - - #Check to see if apt-get update has already been run today - #it needs to have been run at least once on new installs! - timestamp=$(stat -c %Y ${PKG_CACHE}) - timestampAsDate=$(date -d @"${timestamp}" "+%b %e") - today=$(date "+%b %e") - - if [ ! "${today}" == "${timestampAsDate}" ]; then - #update package lists - echo ":::" - echo -n "::: ${PKG_MANAGER} update has not been run today. Running now..." - ${UPDATE_PKG_CACHE} &> /dev/null & spinner $! - echo " done!" - fi -} - -notify_package_updates_available() { - # Let user know if they have outdated packages on their system and - # advise them to run a package update at soonest possible. - echo ":::" - echo -n "::: Checking ${PKG_MANAGER} for upgraded packages...." - updatesToInstall=$(eval "${PKG_COUNT}") - echo " done!" - echo ":::" - if [[ ${updatesToInstall} -eq "0" ]]; then - echo "::: Your system is up to date! Continuing with Pi-hole installation..." - else - echo "::: There are ${updatesToInstall} updates available for your system!" - echo "::: We recommend you run '${PKG_UPDATE}' after installing Pi-Hole! " - echo ":::" - fi -} - -install_dependent_packages() { - # Install packages passed in via argument array - # No spinner - conflicts with set -e - declare -a argArray1=("${!1}") - - for i in "${argArray1[@]}"; do - echo -n "::: Checking for $i..." - package_check_install "${i}" &> /dev/null - echo " installed!" - done -} - -CreateLogFile() { - # Create logfiles if necessary - echo ":::" - echo -n "::: Creating log file and changing owner to dnsmasq..." - if [ ! -f /var/log/pihole.log ]; then - touch /var/log/pihole.log - chmod 644 /var/log/pihole.log - chown "${DNSMASQ_USER}":root /var/log/pihole.log - echo " done!" - else - echo " already exists!" - fi -} - -installPiholeWeb() { - # Install the web interface - echo ":::" - echo "::: Installing pihole custom index page..." - if [ -d "/var/www/html/pihole" ]; then - if [ -f "/var/www/html/pihole/index.html" ]; then - echo "::: Existing index.html detected, not overwriting" - else - echo -n "::: index.html missing, replacing... " - cp /etc/.pihole/advanced/index.html /var/www/html/pihole/ - echo " done!" - fi - - if [ -f "/var/www/html/pihole/index.js" ]; then - echo "::: Existing index.js detected, not overwriting" - else - echo -n "::: index.js missing, replacing... " - cp /etc/.pihole/advanced/index.js /var/www/html/pihole/ - echo " done!" - fi - - else - mkdir /var/www/html/pihole - if [ -f /var/www/html/index.lighttpd.html ]; then - mv /var/www/html/index.lighttpd.html /var/www/html/index.lighttpd.orig - else - printf "\n:::\tNo default index.lighttpd.html file found... not backing up" - fi - cp /etc/.pihole/advanced/index.* /var/www/html/pihole/. - echo " done!" - fi - # Install Sudoer file - echo ":::" - echo -n "::: Installing sudoer file..." - mkdir -p /etc/sudoers.d/ - cp /etc/.pihole/advanced/pihole.sudo /etc/sudoers.d/pihole - chmod 0440 /etc/sudoers.d/pihole - echo " done!" -} - -installCron() { - # Install the cron job - echo ":::" - echo -n "::: Installing latest Cron script..." - cp /etc/.pihole/advanced/pihole.cron /etc/cron.d/pihole - echo " done!" -} - -runGravity() { - # Run gravity.sh to build blacklists - echo ":::" - echo "::: Preparing to run gravity.sh to refresh hosts..." - if ls /etc/pihole/list* 1> /dev/null 2>&1; then - echo "::: Cleaning up previous install (preserving whitelist/blacklist)" - rm /etc/pihole/list.* - fi - echo "::: Running gravity.sh" - /opt/pihole/gravity.sh -} - -create_pihole_user() { - # Check if user pihole exists and create if not - echo "::: Checking if user 'pihole' exists..." - id -u pihole &> /dev/null && echo "::: User 'pihole' already exists" || (echo "::: User 'pihole' doesn't exist. Creating..." && useradd -r -s /usr/sbin/nologin pihole) -} - -configureFirewall() { - # Allow HTTP and DNS traffic - if [ -x "$(command -v firewall-cmd)" ]; then - firewall-cmd --state &> /dev/null && ( echo "::: Configuring firewalld for httpd and dnsmasq.." && firewall-cmd --permanent --add-port=80/tcp && firewall-cmd --permanent --add-port=53/tcp \ - && firewall-cmd --permanent --add-port=53/udp && firewall-cmd --reload) || echo "::: FirewallD not enabled" - elif [ -x "$(command -v iptables)" ]; then - echo "::: Configuring iptables for httpd and dnsmasq.." - iptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT - iptables -A INPUT -p tcp -m tcp --dport 53 -j ACCEPT - iptables -A INPUT -p udp -m udp --dport 53 -j ACCEPT - else - echo "::: No firewall detected.. skipping firewall configuration." - fi -} - -finalExports() { - # Update variables in setupVars.conf file - if [ -e "${setupVars}" ]; then - sed -i.update.bak '/PIHOLE_INTERFACE/d;/IPV4_ADDRESS/d;/IPV6_ADDRESS/d;/PIHOLE_DNS_1/d;/PIHOLE_DNS_2/d;/QUERY_LOGGING/d;' "${setupVars}" - fi - { - echo "PIHOLE_INTERFACE=${PIHOLE_INTERFACE}" - echo "IPV4_ADDRESS=${IPV4_ADDRESS}" - echo "IPV6_ADDRESS=${IPV6_ADDRESS}" - echo "PIHOLE_DNS_1=${PIHOLE_DNS_1}" - echo "PIHOLE_DNS_2=${PIHOLE_DNS_2}" - echo "QUERY_LOGGING=${QUERY_LOGGING}" - }>> "${setupVars}" -} - -installPihole() { - # Install base files and web interface - create_pihole_user - if [ ! -d "/var/www/html" ]; then - mkdir -p /var/www/html - fi - chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/www/html - chmod 775 /var/www/html - usermod -a -G ${LIGHTTPD_GROUP} pihole - if [ -x "$(command -v lighty-enable-mod)" ]; then - lighty-enable-mod fastcgi fastcgi-php > /dev/null || true - else - printf "\n:::\tWarning: 'lighty-enable-mod' utility not found. Please ensure fastcgi is enabled if you experience issues.\n" - fi - installScripts - installConfigs - CreateLogFile - configureSelinux - installPiholeWeb - installCron - configureFirewall - finalExports - runGravity -} - -accountForRefactor() { - # At some point in the future this list can be pruned, for now we'll need it to ensure updates don't break. - - # Refactoring of install script has changed the name of a couple of variables. Sort them out here. - - sed -i 's/piholeInterface/PIHOLE_INTERFACE/g' ${setupVars} - sed -i 's/IPv4_address/IPV4_ADDRESS/g' ${setupVars} - sed -i 's/IPv4addr/IPV4_ADDRESS/g' ${setupVars} - sed -i 's/IPv6_address/IPV6_ADDRESS/g' ${setupVars} - sed -i 's/piholeIPv6/IPV6_ADDRESS/g' ${setupVars} - sed -i 's/piholeDNS1/PIHOLE_DNS_1/g' ${setupVars} - sed -i 's/piholeDNS2/PIHOLE_DNS_2/g' ${setupVars} - -} - -updatePihole() { - accountForRefactor - # Source ${setupVars} for use in the rest of the functions. - . ${setupVars} - # Install base files and web interface - installScripts - installConfigs - CreateLogFile - configureSelinux - installPiholeWeb - installCron - configureFirewall - finalExports #re-export setupVars.conf to account for any new vars added in new versions - runGravity -} - -configureSelinux() { - if [ -x "$(command -v getenforce)" ]; then - printf "\n::: SELinux Detected\n" - printf ":::\tChecking for SELinux policy development packages..." - package_check_install "selinux-policy-devel" > /dev/null - echo " installed!" - printf ":::\tEnabling httpd server side includes (SSI).. " - setsebool -P httpd_ssi_exec on &> /dev/null && echo "Success" || echo "SELinux not enabled" - printf "\n:::\tCompiling Pi-Hole SELinux policy..\n" - if ! [ -x "$(command -v systemctl)" ]; then - sed -i.bak '/systemd/d' /etc/.pihole/advanced/selinux/pihole.te - fi - checkmodule -M -m -o /etc/pihole/pihole.mod /etc/.pihole/advanced/selinux/pihole.te - semodule_package -o /etc/pihole/pihole.pp -m /etc/pihole/pihole.mod - semodule -i /etc/pihole/pihole.pp - rm -f /etc/pihole/pihole.mod - semodule -l | grep pihole &> /dev/null && echo "::: Installed Pi-Hole SELinux policy" || echo "::: Warning: Pi-Hole SELinux policy did not install." - fi -} - -displayFinalMessage() { - if (( ${#1} > 0 )) ; then - # Final completion message to user - whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: - -IPv4: ${IPV4_ADDRESS%/*} -IPv6: ${IPV6_ADDRESS} - -If you set a new IP address, you should restart the Pi. - -The install log is in /etc/pihole. -View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin -The currently set password is ${1}" ${r} ${c} - else - whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: - -IPv4: ${IPV4_ADDRESS%/*} -IPv6: ${IPV6_ADDRESS} - -If you set a new IP address, you should restart the Pi. - -The install log is in /etc/pihole. -View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin" ${r} ${c} - fi -} - -update_dialogs() { - # reconfigure - if [ "${reconfigure}" = true ]; then - opt1a="Repair" - opt1b="This will retain existing settings" - strAdd="You will remain on the same version" - else - opt1a="Update" - opt1b="This will retain existing settings." - strAdd="You will be updated to the latest version." - fi - opt2a="Reconfigure" - opt2b="This will allow you to enter new settings" - - UpdateCmd=$(whiptail --title "Existing Install Detected!" --menu "\n\nWe have detected an existing install.\n\nPlease choose from the following options: \n($strAdd)" ${r} ${c} 2 \ - "${opt1a}" "${opt1b}" \ - "${opt2a}" "${opt2b}" 3>&2 2>&1 1>&3) - - if [[ $? = 0 ]];then - case ${UpdateCmd} in - ${opt1a}) - echo "::: ${opt1a} option selected." - useUpdateVars=true - ;; - ${opt2a}) - echo "::: ${opt2a} option selected" - useUpdateVars=false - ;; - esac - else - echo "::: Cancel selected. Exiting..." - exit 1 - fi - -} - -main() { -# Check arguments for the undocumented flags - for var in "$@"; do - case "$var" in - "--reconfigure" ) reconfigure=true;; - "--i_do_not_follow_recommendations" ) skipSpaceCheck=false;; - "--unattended" ) runUnattended=true;; - esac - done - - if [[ -f ${setupVars} ]]; then - if [[ "${runUnattended}" == true ]]; then - echo "::: --unattended passed to install script, no whiptail dialogs will be displayed" - useUpdateVars=true - else - update_dialogs - fi - fi - - # Start the installer - # Verify there is enough disk space for the install - if [[ "${skipSpaceCheck}" == true ]]; then - echo "::: --i_do_not_follow_recommendations passed to script, skipping free disk space verification!" - else - verifyFreeDiskSpace - fi - - # Update package cache - update_pacakge_cache - - # Notify user of package availability - notify_package_updates_available - - # Install packages used by this installation script - install_dependent_packages INSTALLER_DEPS[@] - - if [[ "${reconfigure}" == true ]]; then - echo "::: --reconfigure passed to install script. Not downloading/updating local repos" - else - # Get Git files for Core and Admin - getGitFiles ${PI_HOLE_LOCAL_REPO} ${piholeGitUrl} - getGitFiles ${webInterfaceDir} ${webInterfaceGitUrl} - fi - - if [[ ${useUpdateVars} == false ]]; then - # Display welcome dialogs - welcomeDialogs - # Create directory for Pi-hole storage - mkdir -p /etc/pihole/ - # Remove legacy scripts from previous storage location - remove_legacy_scripts - # Stop resolver and webserver while installing proceses - stop_service dnsmasq - stop_service lighttpd - # Determine available interfaces - get_available_interfaces - # Find interfaces and let the user choose one - chooseInterface - # Let the user decide if they want to block ads over IPv4 and/or IPv6 - use4andor6 - # Decide what upstream DNS Servers to use - setDNS - # Let the user decide if they want query logging enabled... - setLogging - - # Install packages used by the Pi-hole - install_dependent_packages PIHOLE_DEPS[@] - - # Install and log everything to a file - installPihole | tee ${tmpLog} - else - # update packages used by the Pi-hole - install_dependent_packages PIHOLE_DEPS[@] - - updatePihole | tee ${tmpLog} - fi - - # Move the log file into /etc/pihole for storage - mv ${tmpLog} ${instalLogLoc} - - # Add password to web UI if there is none - pw="" - if [[ $(grep 'WEBPASSWORD' -c /etc/pihole/setupVars.conf) == 0 ]] ; then - pw=$(tr -dc _A-Z-a-z-0-9 < /dev/urandom | head -c 8) - pihole -a -p ${pw} - fi - - if [[ "${useUpdateVars}" == false ]]; then - displayFinalMessage ${pw} - fi - - echo "::: Restarting services..." - # Start services - start_service dnsmasq - enable_service dnsmasq - start_service lighttpd - enable_service lighttpd - echo "::: done." - - echo ":::" - if [[ "${useUpdateVars}" == false ]]; then - echo "::: Installation Complete! Configure your devices to use the Pi-hole as their DNS server using:" - echo "::: ${IPV4_ADDRESS%/*}" - echo "::: ${IPV6_ADDRESS}" - echo ":::" - echo "::: If you set a new IP address, you should restart the Pi." - echo "::: View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin" - else - echo "::: Update complete!" - fi - - if (( ${#pw} > 0 )) ; then - echo ":::" - echo "::: Note: As security measure a password has been installed for your web interface" - echo "::: The currently set password is" - echo "::: ${pw}" - echo ":::" - echo "::: You can always change it using" - echo "::: pihole -a -p new_password" - fi - - echo ":::" - echo "::: The install log is located at: /etc/pihole/install.log" -} - -if [[ "${PH_TEST}" != true ]] ; then - main "$@" -fi +#!/usr/bin/env bash # Pi-hole: A black hole for Internet advertisements # (c) 2015, 2016 by Jacob Salmela # Network-wide ad blocking via your Raspberry Pi # http://pi-hole.net # Installs Pi-hole # # Pi-hole is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # pi-hole.net/donate # # Install with this command (from your Pi): # # curl -L install.pi-hole.net | bash set -e ######## VARIABLES ######### tmpLog=/tmp/pihole-install.log instalLogLoc=/etc/pihole/install.log setupVars=/etc/pihole/setupVars.conf webInterfaceGitUrl="https://github.com/pi-hole/AdminLTE.git" webInterfaceDir="/var/www/html/admin" piholeGitUrl="https://github.com/pi-hole/pi-hole.git" PI_HOLE_LOCAL_REPO="/etc/.pihole" PI_HOLE_FILES=(chronometer list piholeDebug piholeLogFlush setupLCD update version) useUpdateVars=false IPV4_ADDRESS="" IPV6_ADDRESS="" QUERY_LOGGING=true # Find the rows and columns will default to 80x24 is it can not be detected screen_size=$(stty size 2>/dev/null || echo 24 80) rows=$(echo $screen_size | awk '{print $1}') columns=$(echo $screen_size | awk '{print $2}') # Divide by two so the dialogs take up half of the screen, which looks nice. r=$(( rows / 2 )) c=$(( columns / 2 )) # Unless the screen is tiny r=$(( r < 20 ? 20 : r )) c=$(( c < 70 ? 70 : c )) ######## Undocumented Flags. Shhh ######## skipSpaceCheck=false reconfigure=false runUnattended=false ######## FIRST CHECK ######## # Must be root to install echo ":::" if [[ ${EUID} -eq 0 ]]; then echo "::: You are root." else echo "::: Script called with non-root privileges. The Pi-hole installs server packages and configures" echo "::: system networking, it requires elevated rights. Please check the contents of the script for" echo "::: any concerns with this requirement. Please be sure to download this script from a trusted source." echo ":::" echo "::: Detecting the presence of the sudo utility for continuation of this install..." if [ -x "$(command -v sudo)" ]; then echo "::: Utility sudo located." exec curl -sSL https://install.pi-hole.net | sudo bash "$@" exit $? else echo "::: sudo is needed for the Web interface to run pihole commands. Please run this script as root and it will be automatically installed." exit 1 fi fi # Compatibility if [[ $(command -v apt-get) ]]; then #Debian Family ############################################# PKG_MANAGER="apt-get" PKG_CACHE="/var/lib/apt/lists/" UPDATE_PKG_CACHE="${PKG_MANAGER} update" PKG_UPDATE="${PKG_MANAGER} upgrade" PKG_INSTALL="${PKG_MANAGER} --yes --fix-missing install" # grep -c will return 1 retVal on 0 matches, block this throwing the set -e with an OR TRUE PKG_COUNT="${PKG_MANAGER} -s -o Debug::NoLocking=true upgrade | grep -c ^Inst || true" # ######################################### # fixes for dependancy differences # Debian 7 doesn't have iproute2 use iproute ${PKG_MANAGER} install --dry-run iproute2 > /dev/null 2>&1 && IPROUTE_PKG="iproute2" || IPROUTE_PKG="iproute" # Prefer the php metapackage if it's there, fall back on the php5 pacakges ${PKG_MANAGER} install --dry-run php > /dev/null 2>&1 && phpVer="php" || phpVer="php5" # ######################################### INSTALLER_DEPS=( apt-utils whiptail git dhcpcd5) PIHOLE_DEPS=( iputils-ping lsof dnsutils bc dnsmasq lighttpd ${phpVer}-common ${phpVer}-cgi curl unzip wget sudo netcat cron ${IPROUTE_PKG} ) LIGHTTPD_USER="www-data" LIGHTTPD_GROUP="www-data" LIGHTTPD_CFG="lighttpd.conf.debian" DNSMASQ_USER="dnsmasq" package_check_install() { dpkg-query -W -f='${Status}' "${1}" 2>/dev/null | grep -c "ok installed" || ${PKG_INSTALL} "${1}" } elif [ $(command -v rpm) ]; then # Fedora Family if [ $(command -v dnf) ]; then PKG_MANAGER="dnf" else PKG_MANAGER="yum" fi PKG_CACHE="/var/cache/${PKG_MANAGER}" UPDATE_PKG_CACHE="${PKG_MANAGER} check-update" PKG_UPDATE="${PKG_MANAGER} update -y" PKG_INSTALL="${PKG_MANAGER} install -y" PKG_COUNT="${PKG_MANAGER} check-update | egrep '(.i686|.x86|.noarch|.arm|.src)' | wc -l" INSTALLER_DEPS=( iproute net-tools procps-ng newt git ) PIHOLE_DEPS=( epel-release bind-utils bc dnsmasq lighttpd lighttpd-fastcgi php-common php-cli php curl unzip wget findutils cronie sudo nmap-ncat ) if grep -q 'Fedora' /etc/redhat-release; then remove_deps=(epel-release); PIHOLE_DEPS=( ${PIHOLE_DEPS[@]/$remove_deps} ); fi LIGHTTPD_USER="lighttpd" LIGHTTPD_GROUP="lighttpd" LIGHTTPD_CFG="lighttpd.conf.fedora" DNSMASQ_USER="nobody" package_check_install() { rpm -qa | grep ^"${1}"- > /dev/null || ${PKG_INSTALL} "${1}" } else echo "OS distribution not supported" exit fi ####### FUNCTIONS ########## spinner() { local pid=$1 local delay=0.50 local spinstr='/-\|' while [ "$(ps a | awk '{print $1}' | grep "${pid}")" ]; do local temp=${spinstr#?} printf " [%c] " "${spinstr}" local spinstr=${temp}${spinstr%"$temp"} sleep ${delay} printf "\b\b\b\b\b\b" done printf " \b\b\b\b" } is_repo() { # Use git to check if directory is currently under VCS, return the value local directory="${1}" git -C "${directory}" status --short &> /dev/null return } make_repo() { local directory="${1}" local remoteRepo="${2}" # Remove the non-repod interface and clone the interface echo -n "::: Cloning $remoteRepo into $directory..." rm -rf "${directory}" git clone -q --depth 1 "${remoteRepo}" "${directory}" > /dev/null & spinner $! echo " done!" } update_repo() { local directory="${1}" # Pull the latest commits echo -n "::: Updating repo in $1..." cd "${directory}" || exit 1 git stash -q > /dev/null & spinner $! git pull -q > /dev/null & spinner $! echo " done!" } getGitFiles() { # Setup git repos for directory and repository passed # as arguments 1 and 2 local directory="${1}" local remoteRepo="${2}" echo ":::" echo "::: Checking for existing repository..." if is_repo "${directory}"; then update_repo "${directory}" else make_repo "${directory}" "${remoteRepo}" fi } find_IPv4_information() { # Find IP used to route to outside world IPv4dev=$(ip route get 8.8.8.8 | awk '{for(i=1;i<=NF;i++)if($i~/dev/)print $(i+1)}') IPV4_ADDRESS=$(ip -o -f inet addr show dev "$IPv4dev" | awk '{print $4}' | awk 'END {print}') IPv4gw=$(ip route get 8.8.8.8 | awk '{print $3}') } get_available_interfaces() { # Get available interfaces. Consider only getting UP interfaces in the future, and leaving DOWN interfaces out of list. availableInterfaces=$(ip -o link | awk '{print $2}' | grep -v "lo" | cut -d':' -f1 | cut -d'@' -f1) } welcomeDialogs() { # Display the welcome dialog whiptail --msgbox --backtitle "Welcome" --title "Pi-hole automated installer" "\n\nThis installer will transform your device into a network-wide ad blocker!" ${r} ${c} # Support for a part-time dev whiptail --msgbox --backtitle "Plea" --title "Free and open source" "\n\nThe Pi-hole is free, but powered by your donations: http://pi-hole.net/donate" ${r} ${c} # Explain the need for a static address whiptail --msgbox --backtitle "Initiating network interface" --title "Static IP Needed" "\n\nThe Pi-hole is a SERVER so it needs a STATIC IP ADDRESS to function properly. In the next section, you can choose to use your current network settings (DHCP) or to manually edit them." ${r} ${c} } verifyFreeDiskSpace() { # 50MB is the minimum space needed (45MB install (includes web admin bootstrap/jquery libraries etc) + 5MB one day of logs.) # - Fourdee: Local ensures the variable is only created, and accessible within this function/void. Generally considered a "good" coding practice for non-global variables. echo "::: Verifying free disk space..." local required_free_kilobytes=51200 local existing_free_kilobytes=$(df -Pk | grep -m1 '\/$' | awk '{print $4}') # - Unknown free disk space , not a integer if ! [[ "${existing_free_kilobytes}" =~ ^([0-9])+$ ]]; then echo "::: Unknown free disk space!" echo "::: We were unable to determine available free disk space on this system." echo "::: You may override this check and force the installation, however, it is not recommended" echo "::: To do so, pass the argument '--i_do_not_follow_recommendations' to the install script" echo "::: eg. curl -L https://install.pi-hole.net | bash /dev/stdin --i_do_not_follow_recommendations" exit 1 # - Insufficient free disk space elif [[ ${existing_free_kilobytes} -lt ${required_free_kilobytes} ]]; then echo "::: Insufficient Disk Space!" echo "::: Your system appears to be low on disk space. pi-hole recommends a minimum of $required_free_kilobytes KiloBytes." echo "::: You only have ${existing_free_kilobytes} KiloBytes free." echo "::: If this is a new install you may need to expand your disk." echo "::: Try running 'sudo raspi-config', and choose the 'expand file system option'" echo "::: After rebooting, run this installation again. (curl -L https://install.pi-hole.net | bash)" echo "Insufficient free space, exiting..." exit 1 fi } chooseInterface() { # Turn the available interfaces into an array so it can be used with a whiptail dialog local interfacesArray=() # Number of available interfaces local interfaceCount # Whiptail variable storage local chooseInterfaceCmd # Temporary Whiptail options storage local chooseInterfaceOptions # Loop sentinel variable local firstLoop=1 while read -r line; do mode="OFF" if [[ ${firstLoop} -eq 1 ]]; then firstLoop=0 mode="ON" fi interfacesArray+=("${line}" "available" "${mode}") done <<< "${availableInterfaces}" # Find out how many interfaces are available to choose from interfaceCount=$(echo "${availableInterfaces}" | wc -l) chooseInterfaceCmd=(whiptail --separate-output --radiolist "Choose An Interface (press space to select)" ${r} ${c} ${interfaceCount}) chooseInterfaceOptions=$("${chooseInterfaceCmd[@]}" "${interfacesArray[@]}" 2>&1 >/dev/tty) if [[ $? = 0 ]]; then for desiredInterface in ${chooseInterfaceOptions}; do PIHOLE_INTERFACE=${desiredInterface} echo "::: Using interface: $PIHOLE_INTERFACE" done else echo "::: Cancel selected, exiting...." exit 1 fi } useIPv6dialog() { # Show the IPv6 address used for blocking IPV6_ADDRESS=$(ip -6 route get 2001:4860:4860::8888 | awk -F " " '{ for(i=1;i<=NF;i++) if ($i == "src") print $(i+1) }') whiptail --msgbox --backtitle "IPv6..." --title "IPv6 Supported" "$IPV6_ADDRESS will be used to block ads." ${r} ${c} } use4andor6() { local useIPv4 local useIPv6 # Let use select IPv4 and/or IPv6 cmd=(whiptail --separate-output --checklist "Select Protocols (press space to select)" ${r} ${c} 2) options=(IPv4 "Block ads over IPv4" on IPv6 "Block ads over IPv6" on) choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty) if [[ $? = 0 ]];then for choice in ${choices} do case ${choice} in IPv4 ) useIPv4=true;; IPv6 ) useIPv6=true;; esac done if [[ ${useIPv4} ]]; then find_IPv4_information getStaticIPv4Settings setStaticIPv4 fi if [[ ${useIPv6} ]]; then useIPv6dialog fi echo "::: IPv4 address: ${IPV4_ADDRESS}" echo "::: IPv6 address: ${IPV6_ADDRESS}" if [ ! ${useIPv4} ] && [ ! ${useIPv6} ]; then echo "::: Cannot continue, neither IPv4 or IPv6 selected" echo "::: Exiting" exit 1 fi else echo "::: Cancel selected. Exiting..." exit 1 fi } getStaticIPv4Settings() { # Ask if the user wants to use DHCP settings as their static IP if (whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Do you want to use your current network settings as a static address? IP address: ${IPV4_ADDRESS} Gateway: ${IPv4gw}" ${r} ${c}); then # If they choose yes, let the user know that the IP address will not be available via DHCP and may cause a conflict. whiptail --msgbox --backtitle "IP information" --title "FYI: IP Conflict" "It is possible your router could still try to assign this IP to a device, which would cause a conflict. But in most cases the router is smart enough to not do that. If you are worried, either manually set the address, or modify the DHCP reservation pool so it does not include the IP you want. It is also possible to use a DHCP reservation, but if you are going to do that, you might as well set a static address." ${r} ${c} # Nothing else to do since the variables are already set above else # Otherwise, we need to ask the user to input their desired settings. # Start by getting the IPv4 address (pre-filling it with info gathered from DHCP) # Start a loop to let the user enter their information with the chance to go back and edit it if necessary until [[ ${ipSettingsCorrect} = True ]]; do # Ask for the IPv4 address IPV4_ADDRESS=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 address" --inputbox "Enter your desired IPv4 address" ${r} ${c} "${IPV4_ADDRESS}" 3>&1 1>&2 2>&3) if [[ $? = 0 ]]; then echo "::: Your static IPv4 address: ${IPV4_ADDRESS}" # Ask for the gateway IPv4gw=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 gateway (router)" --inputbox "Enter your desired IPv4 default gateway" ${r} ${c} "${IPv4gw}" 3>&1 1>&2 2>&3) if [[ $? = 0 ]]; then echo "::: Your static IPv4 gateway: ${IPv4gw}" # Give the user a chance to review their settings before moving on if (whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Are these settings correct? IP address: ${IPV4_ADDRESS} Gateway: ${IPv4gw}" ${r} ${c}); then # After that's done, the loop ends and we move on ipSettingsCorrect=True else # If the settings are wrong, the loop continues ipSettingsCorrect=False fi else # Cancelling gateway settings window ipSettingsCorrect=False echo "::: Cancel selected. Exiting..." exit 1 fi else # Cancelling IPv4 settings window ipSettingsCorrect=False echo "::: Cancel selected. Exiting..." exit 1 fi done # End the if statement for DHCP vs. static fi } setDHCPCD() { # Append these lines to dhcpcd.conf to enable a static IP echo "## interface ${PIHOLE_INTERFACE} static ip_address=${IPV4_ADDRESS} static routers=${IPv4gw} static domain_name_servers=${IPv4gw}" | tee -a /etc/dhcpcd.conf >/dev/null } setStaticIPv4() { local IFCFG_FILE local IPADDR local CIDR if [[ -f /etc/dhcpcd.conf ]]; then # Debian Family if grep -q "${IPV4_ADDRESS}" /etc/dhcpcd.conf; then echo "::: Static IP already configured" else setDHCPCD ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" echo ":::" echo "::: Setting IP to ${IPV4_ADDRESS}. You may need to restart after the install is complete." echo ":::" fi elif [[ -f /etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE} ]];then # Fedora Family IFCFG_FILE=/etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE} if grep -q "${IPV4_ADDRESS}" "${IFCFG_FILE}"; then echo "::: Static IP already configured" else IPADDR=$(echo "${IPV4_ADDRESS}" | cut -f1 -d/) CIDR=$(echo "${IPV4_ADDRESS}" | cut -f2 -d/) # Backup existing interface configuration: cp "${IFCFG_FILE}" "${IFCFG_FILE}".pihole.orig # Build Interface configuration file: { echo "# Configured via Pi-Hole installer" echo "DEVICE=$PIHOLE_INTERFACE" echo "BOOTPROTO=none" echo "ONBOOT=yes" echo "IPADDR=$IPADDR" echo "PREFIX=$CIDR" echo "GATEWAY=$IPv4gw" echo "DNS1=$PIHOLE_DNS_1" echo "DNS2=$PIHOLE_DNS_2" echo "USERCTL=no" }> "${IFCFG_FILE}" ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" if [ -x "$(command -v nmcli)" ];then # Tell NetworkManager to read our new sysconfig file nmcli con load "${IFCFG_FILE}" > /dev/null fi echo ":::" echo "::: Setting IP to ${IPV4_ADDRESS}. You may need to restart after the install is complete." echo ":::" fi else echo "::: Warning: Unable to locate configuration file to set static IPv4 address!" exit 1 fi } valid_ip() { local ip=${1} local stat=1 if [[ ${ip} =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then OIFS=$IFS IFS='.' ip=(${ip}) IFS=${OIFS} [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \ && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]] stat=$? fi return ${stat} } setDNS() { DNSChooseCmd=(whiptail --separate-output --radiolist "Select Upstream DNS Provider. To use your own, select Custom." ${r} ${c} 6) DNSChooseOptions=(Google "" on OpenDNS "" off Level3 "" off Norton "" off Comodo "" off Custom "" off) DNSchoices=$("${DNSChooseCmd[@]}" "${DNSChooseOptions[@]}" 2>&1 >/dev/tty) if [[ $? = 0 ]];then case ${DNSchoices} in Google) echo "::: Using Google DNS servers." PIHOLE_DNS_1="8.8.8.8" PIHOLE_DNS_2="8.8.4.4" ;; OpenDNS) echo "::: Using OpenDNS servers." PIHOLE_DNS_1="208.67.222.222" PIHOLE_DNS_2="208.67.220.220" ;; Level3) echo "::: Using Level3 servers." PIHOLE_DNS_1="4.2.2.1" PIHOLE_DNS_2="4.2.2.2" ;; Norton) echo "::: Using Norton ConnectSafe servers." PIHOLE_DNS_1="199.85.126.10" PIHOLE_DNS_2="199.85.127.10" ;; Comodo) echo "::: Using Comodo Secure servers." PIHOLE_DNS_1="8.26.56.26" PIHOLE_DNS_2="8.20.247.20" ;; Custom) until [[ ${DNSSettingsCorrect} = True ]]; do strInvalid="Invalid" if [ ! ${PIHOLE_DNS_1} ]; then if [ ! ${PIHOLE_DNS_2} ]; then prePopulate="" else prePopulate=", ${PIHOLE_DNS_2}" fi elif [ ${PIHOLE_DNS_1} ] && [ ! ${PIHOLE_DNS_2} ]; then prePopulate="${PIHOLE_DNS_1}" elif [ ${PIHOLE_DNS_1} ] && [ ${PIHOLE_DNS_2} ]; then prePopulate="${PIHOLE_DNS_1}, ${PIHOLE_DNS_2}" fi piholeDNS=$(whiptail --backtitle "Specify Upstream DNS Provider(s)" --inputbox "Enter your desired upstream DNS provider(s), seperated by a comma.\n\nFor example '8.8.8.8, 8.8.4.4'" ${r} ${c} "${prePopulate}" 3>&1 1>&2 2>&3) if [[ $? = 0 ]]; then PIHOLE_DNS_1=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$1}') PIHOLE_DNS_2=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$2}') if ! valid_ip "${PIHOLE_DNS_1}" || [ ! "${PIHOLE_DNS_1}" ]; then PIHOLE_DNS_1=${strInvalid} fi if ! valid_ip "${PIHOLE_DNS_2}" && [ "${PIHOLE_DNS_2}" ]; then PIHOLE_DNS_2=${strInvalid} fi else echo "::: Cancel selected, exiting...." exit 1 fi if [[ ${PIHOLE_DNS_1} == "${strInvalid}" ]] || [[ ${PIHOLE_DNS_2} == "${strInvalid}" ]]; then whiptail --msgbox --backtitle "Invalid IP" --title "Invalid IP" "One or both entered IP addresses were invalid. Please try again.\n\n DNS Server 1: $PIHOLE_DNS_1\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c} if [[ ${PIHOLE_DNS_1} == "${strInvalid}" ]]; then PIHOLE_DNS_1="" fi if [[ ${PIHOLE_DNS_2} == "${strInvalid}" ]]; then PIHOLE_DNS_2="" fi DNSSettingsCorrect=False else if (whiptail --backtitle "Specify Upstream DNS Provider(s)" --title "Upstream DNS Provider(s)" --yesno "Are these settings correct?\n DNS Server 1: $PIHOLE_DNS_1\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c}); then DNSSettingsCorrect=True else # If the settings are wrong, the loop continues DNSSettingsCorrect=False fi fi done ;; esac else echo "::: Cancel selected. Exiting..." exit 1 fi } setLogging() { local LogToggleCommand local LogChooseOptions local LogChoices LogToggleCommand=(whiptail --separate-output --radiolist "Do you want to log queries?\n (Disabling will render graphs on the Admin page useless):" ${r} ${c} 6) LogChooseOptions=("On (Reccomended)" "" on Off "" off) LogChoices=$("${LogToggleCommand[@]}" "${LogChooseOptions[@]}" 2>&1 >/dev/tty) || (echo "::: Cancel selected. Exiting..." && exit 1) case ${LogChoices} in "On (Recommended)") echo "::: Logging On." QUERY_LOGGING=true ;; Off) echo "::: Logging Off." QUERY_LOGGING=false ;; esac } version_check_dnsmasq() { # Check if /etc/dnsmasq.conf is from pihole. If so replace with an original and install new in .d directory local dnsmasq_conf="/etc/dnsmasq.conf" local dnsmasq_conf_orig="/etc/dnsmasq.conf.orig" local dnsmasq_pihole_id_string="addn-hosts=/etc/pihole/gravity.list" local dnsmasq_original_config="/etc/.pihole/advanced/dnsmasq.conf.original" local dnsmasq_pihole_01_snippet="/etc/.pihole/advanced/01-pihole.conf" local dnsmasq_pihole_01_location="/etc/dnsmasq.d/01-pihole.conf" if [ -f ${dnsmasq_conf} ]; then echo -n "::: Existing dnsmasq.conf found..." if grep -q ${dnsmasq_pihole_id_string} ${dnsmasq_conf}; then echo " it is from a previous pi-hole install." echo -n "::: Backing up dnsmasq.conf to dnsmasq.conf.orig..." mv -f ${dnsmasq_conf} ${dnsmasq_conf_orig} echo " done." echo -n "::: Restoring default dnsmasq.conf..." cp ${dnsmasq_original_config} ${dnsmasq_conf} echo " done." else echo " it is not a pi-hole file, leaving alone!" fi else echo -n "::: No dnsmasq.conf found.. restoring default dnsmasq.conf..." cp ${dnsmasq_original_config} ${dnsmasq_conf} echo " done." fi echo -n "::: Copying 01-pihole.conf to /etc/dnsmasq.d/01-pihole.conf..." cp ${dnsmasq_pihole_01_snippet} ${dnsmasq_pihole_01_location} echo " done." sed -i "s/@INT@/$PIHOLE_INTERFACE/" ${dnsmasq_pihole_01_location} if [[ "${PIHOLE_DNS_1}" != "" ]]; then sed -i "s/@DNS1@/$PIHOLE_DNS_1/" ${dnsmasq_pihole_01_location} else sed -i '/^server=@DNS1@/d' ${dnsmasq_pihole_01_location} fi if [[ "${PIHOLE_DNS_2}" != "" ]]; then sed -i "s/@DNS2@/$PIHOLE_DNS_2/" ${dnsmasq_pihole_01_location} else sed -i '/^server=@DNS2@/d' ${dnsmasq_pihole_01_location} fi sed -i 's/^#conf-dir=\/etc\/dnsmasq.d$/conf-dir=\/etc\/dnsmasq.d/' ${dnsmasq_conf} if [[ "${QUERY_LOGGING}" == false ]] ; then #Disable Logging sed -i 's/^log-queries/#log-queries/' ${dnsmasq_pihole_01_location} else #Enable Logging sed -i 's/^#log-queries/log-queries/' ${dnsmasq_pihole_01_location} fi } remove_legacy_scripts() { #Tidy up /usr/local/bin directory if installing over previous install. oldFiles=( gravity chronometer whitelist blacklist piholeLogFlush updateDashboard uninstall setupLCD piholeDebug) for i in "${oldFiles[@]}"; do if [ -f "/usr/local/bin/$i.sh" ]; then rm /usr/local/bin/"$i".sh fi done } clean_existing() { # Clean an exiting installation to prepare for upgrade/reinstall # ${1} Directory to clean; ${2} Array of files to remove local clean_directory="${1}" local old_files=${2} for script in "${old_files[@]}"; do rm -f "${clean_directory}${script}.sh" done } installScripts() { # Install the scripts from repository to their various locations readonly install_dir="/opt/pihole/" echo ":::" echo -n "::: Installing scripts from ${PI_HOLE_LOCAL_REPO}..." # Clear out script files from Pi-hole scripts directory. clean_existing "${install_dir}" "${PI_HOLE_FILES}" # Install files from local core repository if is_repo "${PI_HOLE_LOCAL_REPO}"; then cd "${PI_HOLE_LOCAL_REPO}" install -o "${USER}" -Dm755 -t /opt/pihole/ gravity.sh install -o "${USER}" -Dm755 -t /opt/pihole/ ./advanced/Scripts/*.sh install -o "${USER}" -Dm755 -t /opt/pihole/ ./automated\ install/uninstall.sh install -o "${USER}" -Dm755 -t /usr/local/bin/ pihole install -Dm644 ./advanced/bash-completion/pihole /etc/bash_completion.d/pihole echo " done." else echo " *** ERROR: Local repo ${core_repo} not found, exiting." exit 1 fi } installConfigs() { # Install the configs from /etc/.pihole to their various locations echo ":::" echo "::: Installing configs..." version_check_dnsmasq if [ ! -d "/etc/lighttpd" ]; then mkdir /etc/lighttpd chown "${USER}":root /etc/lighttpd elif [ -f "/etc/lighttpd/lighttpd.conf" ]; then mv /etc/lighttpd/lighttpd.conf /etc/lighttpd/lighttpd.conf.orig fi cp /etc/.pihole/advanced/${LIGHTTPD_CFG} /etc/lighttpd/lighttpd.conf mkdir -p /var/run/lighttpd chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/run/lighttpd mkdir -p /var/cache/lighttpd/compress chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/compress mkdir -p /var/cache/lighttpd/uploads chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/uploads } stop_service() { # Stop service passed in as argument. # Can softfail, as process may not be installed when this is called echo ":::" echo -n "::: Stopping ${1} service..." if [ -x "$(command -v systemctl)" ]; then systemctl stop "${1}" &> /dev/null & spinner $! || true else service "${1}" stop &> /dev/null & spinner $! || true fi echo " done." } start_service() { # Start/Restart service passed in as argument # This should not fail, it's an error if it does echo ":::" echo -n "::: Starting ${1} service..." if [ -x "$(command -v systemctl)" ]; then systemctl restart "${1}" &> /dev/null & spinner $! else service "${1}" restart &> /dev/null & spinner $! fi echo " done." } enable_service() { # Enable service so that it will start with next reboot echo ":::" echo -n "::: Enabling ${1} service to start on reboot..." if [ -x "$(command -v systemctl)" ]; then systemctl enable "${1}" &> /dev/null & spinner $! else update-rc.d "${1}" defaults &> /dev/null & spinner $! fi echo " done." } update_pacakge_cache() { #Running apt-get update/upgrade with minimal output can cause some issues with #requiring user input (e.g password for phpmyadmin see #218) #Check to see if apt-get update has already been run today #it needs to have been run at least once on new installs! timestamp=$(stat -c %Y ${PKG_CACHE}) timestampAsDate=$(date -d @"${timestamp}" "+%b %e") today=$(date "+%b %e") if [ ! "${today}" == "${timestampAsDate}" ]; then #update package lists echo ":::" echo -n "::: ${PKG_MANAGER} update has not been run today. Running now..." ${UPDATE_PKG_CACHE} &> /dev/null & spinner $! echo " done!" fi } notify_package_updates_available() { # Let user know if they have outdated packages on their system and # advise them to run a package update at soonest possible. echo ":::" echo -n "::: Checking ${PKG_MANAGER} for upgraded packages...." updatesToInstall=$(eval "${PKG_COUNT}") echo " done!" echo ":::" if [[ ${updatesToInstall} -eq "0" ]]; then echo "::: Your system is up to date! Continuing with Pi-hole installation..." else echo "::: There are ${updatesToInstall} updates available for your system!" echo "::: We recommend you run '${PKG_UPDATE}' after installing Pi-Hole! " echo ":::" fi } install_dependent_packages() { # Install packages passed in via argument array # No spinner - conflicts with set -e declare -a argArray1=("${!1}") for i in "${argArray1[@]}"; do echo -n "::: Checking for $i..." package_check_install "${i}" &> /dev/null echo " installed!" done } CreateLogFile() { # Create logfiles if necessary echo ":::" echo -n "::: Creating log file and changing owner to dnsmasq..." if [ ! -f /var/log/pihole.log ]; then touch /var/log/pihole.log chmod 644 /var/log/pihole.log chown "${DNSMASQ_USER}":root /var/log/pihole.log echo " done!" else echo " already exists!" fi } installPiholeWeb() { # Install the web interface echo ":::" echo "::: Installing pihole custom index page..." if [ -d "/var/www/html/pihole" ]; then if [ -f "/var/www/html/pihole/index.html" ]; then echo "::: Existing index.html detected, not overwriting" else echo -n "::: index.html missing, replacing... " cp /etc/.pihole/advanced/index.html /var/www/html/pihole/ echo " done!" fi if [ -f "/var/www/html/pihole/index.js" ]; then echo "::: Existing index.js detected, not overwriting" else echo -n "::: index.js missing, replacing... " cp /etc/.pihole/advanced/index.js /var/www/html/pihole/ echo " done!" fi else mkdir /var/www/html/pihole if [ -f /var/www/html/index.lighttpd.html ]; then mv /var/www/html/index.lighttpd.html /var/www/html/index.lighttpd.orig else printf "\n:::\tNo default index.lighttpd.html file found... not backing up" fi cp /etc/.pihole/advanced/index.* /var/www/html/pihole/. echo " done!" fi # Install Sudoer file echo ":::" echo -n "::: Installing sudoer file..." mkdir -p /etc/sudoers.d/ cp /etc/.pihole/advanced/pihole.sudo /etc/sudoers.d/pihole chmod 0440 /etc/sudoers.d/pihole echo " done!" } installCron() { # Install the cron job echo ":::" echo -n "::: Installing latest Cron script..." cp /etc/.pihole/advanced/pihole.cron /etc/cron.d/pihole echo " done!" } runGravity() { # Run gravity.sh to build blacklists echo ":::" echo "::: Preparing to run gravity.sh to refresh hosts..." if ls /etc/pihole/list* 1> /dev/null 2>&1; then echo "::: Cleaning up previous install (preserving whitelist/blacklist)" rm /etc/pihole/list.* fi echo "::: Running gravity.sh" /opt/pihole/gravity.sh } create_pihole_user() { # Check if user pihole exists and create if not echo "::: Checking if user 'pihole' exists..." id -u pihole &> /dev/null && echo "::: User 'pihole' already exists" || (echo "::: User 'pihole' doesn't exist. Creating..." && useradd -r -s /usr/sbin/nologin pihole) } configureFirewall() { # Allow HTTP and DNS traffic if [ -x "$(command -v firewall-cmd)" ]; then firewall-cmd --state &> /dev/null && ( echo "::: Configuring firewalld for httpd and dnsmasq.." && firewall-cmd --permanent --add-port=80/tcp && firewall-cmd --permanent --add-port=53/tcp \ && firewall-cmd --permanent --add-port=53/udp && firewall-cmd --reload) || echo "::: FirewallD not enabled" elif [ -x "$(command -v iptables)" ]; then echo "::: Configuring iptables for httpd and dnsmasq.." iptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT iptables -A INPUT -p tcp -m tcp --dport 53 -j ACCEPT iptables -A INPUT -p udp -m udp --dport 53 -j ACCEPT else echo "::: No firewall detected.. skipping firewall configuration." fi } finalExports() { # Update variables in setupVars.conf file if [ -e "${setupVars}" ]; then sed -i.update.bak '/PIHOLE_INTERFACE/d;/IPV4_ADDRESS/d;/IPV6_ADDRESS/d;/PIHOLE_DNS_1/d;/PIHOLE_DNS_2/d;/QUERY_LOGGING/d;' "${setupVars}" fi { echo "PIHOLE_INTERFACE=${PIHOLE_INTERFACE}" echo "IPV4_ADDRESS=${IPV4_ADDRESS}" echo "IPV6_ADDRESS=${IPV6_ADDRESS}" echo "PIHOLE_DNS_1=${PIHOLE_DNS_1}" echo "PIHOLE_DNS_2=${PIHOLE_DNS_2}" echo "QUERY_LOGGING=${QUERY_LOGGING}" }>> "${setupVars}" } installPihole() { # Install base files and web interface create_pihole_user if [ ! -d "/var/www/html" ]; then mkdir -p /var/www/html fi chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/www/html chmod 775 /var/www/html usermod -a -G ${LIGHTTPD_GROUP} pihole if [ -x "$(command -v lighty-enable-mod)" ]; then lighty-enable-mod fastcgi fastcgi-php > /dev/null || true else printf "\n:::\tWarning: 'lighty-enable-mod' utility not found. Please ensure fastcgi is enabled if you experience issues.\n" fi installScripts installConfigs CreateLogFile configureSelinux installPiholeWeb installCron configureFirewall finalExports runGravity } accountForRefactor() { # At some point in the future this list can be pruned, for now we'll need it to ensure updates don't break. # Refactoring of install script has changed the name of a couple of variables. Sort them out here. sed -i 's/piholeInterface/PIHOLE_INTERFACE/g' ${setupVars} sed -i 's/IPv4_address/IPV4_ADDRESS/g' ${setupVars} sed -i 's/IPv4addr/IPV4_ADDRESS/g' ${setupVars} sed -i 's/IPv6_address/IPV6_ADDRESS/g' ${setupVars} sed -i 's/piholeIPv6/IPV6_ADDRESS/g' ${setupVars} sed -i 's/piholeDNS1/PIHOLE_DNS_1/g' ${setupVars} sed -i 's/piholeDNS2/PIHOLE_DNS_2/g' ${setupVars} } updatePihole() { accountForRefactor # Source ${setupVars} for use in the rest of the functions. . ${setupVars} # Install base files and web interface installScripts installConfigs CreateLogFile configureSelinux installPiholeWeb installCron configureFirewall finalExports #re-export setupVars.conf to account for any new vars added in new versions runGravity } configureSelinux() { if [ -x "$(command -v getenforce)" ]; then printf "\n::: SELinux Detected\n" printf ":::\tChecking for SELinux policy development packages..." package_check_install "selinux-policy-devel" > /dev/null echo " installed!" printf ":::\tEnabling httpd server side includes (SSI).. " setsebool -P httpd_ssi_exec on &> /dev/null && echo "Success" || echo "SELinux not enabled" printf "\n:::\tCompiling Pi-Hole SELinux policy..\n" if ! [ -x "$(command -v systemctl)" ]; then sed -i.bak '/systemd/d' /etc/.pihole/advanced/selinux/pihole.te fi checkmodule -M -m -o /etc/pihole/pihole.mod /etc/.pihole/advanced/selinux/pihole.te semodule_package -o /etc/pihole/pihole.pp -m /etc/pihole/pihole.mod semodule -i /etc/pihole/pihole.pp rm -f /etc/pihole/pihole.mod semodule -l | grep pihole &> /dev/null && echo "::: Installed Pi-Hole SELinux policy" || echo "::: Warning: Pi-Hole SELinux policy did not install." fi } displayFinalMessage() { if (( ${#1} > 0 )) ; then # Final completion message to user whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: IPv4: ${IPV4_ADDRESS%/*} IPv6: ${IPV6_ADDRESS} If you set a new IP address, you should restart the Pi. The install log is in /etc/pihole. View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin The currently set password is ${1}" ${r} ${c} else whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: IPv4: ${IPV4_ADDRESS%/*} IPv6: ${IPV6_ADDRESS} If you set a new IP address, you should restart the Pi. The install log is in /etc/pihole. View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin" ${r} ${c} fi } update_dialogs() { # reconfigure if [ "${reconfigure}" = true ]; then opt1a="Repair" opt1b="This will retain existing settings" strAdd="You will remain on the same version" else opt1a="Update" opt1b="This will retain existing settings." strAdd="You will be updated to the latest version." fi opt2a="Reconfigure" opt2b="This will allow you to enter new settings" UpdateCmd=$(whiptail --title "Existing Install Detected!" --menu "\n\nWe have detected an existing install.\n\nPlease choose from the following options: \n($strAdd)" ${r} ${c} 2 \ "${opt1a}" "${opt1b}" \ "${opt2a}" "${opt2b}" 3>&2 2>&1 1>&3) if [[ $? = 0 ]];then case ${UpdateCmd} in ${opt1a}) echo "::: ${opt1a} option selected." useUpdateVars=true ;; ${opt2a}) echo "::: ${opt2a} option selected" useUpdateVars=false ;; esac else echo "::: Cancel selected. Exiting..." exit 1 fi } main() { # Check arguments for the undocumented flags for var in "$@"; do case "$var" in "--reconfigure" ) reconfigure=true;; "--i_do_not_follow_recommendations" ) skipSpaceCheck=false;; "--unattended" ) runUnattended=true;; esac done if [[ -f ${setupVars} ]]; then if [[ "${runUnattended}" == true ]]; then echo "::: --unattended passed to install script, no whiptail dialogs will be displayed" useUpdateVars=true else update_dialogs fi fi # Start the installer # Verify there is enough disk space for the install if [[ "${skipSpaceCheck}" == true ]]; then echo "::: --i_do_not_follow_recommendations passed to script, skipping free disk space verification!" else verifyFreeDiskSpace fi # Update package cache update_pacakge_cache # Notify user of package availability notify_package_updates_available # Install packages used by this installation script install_dependent_packages INSTALLER_DEPS[@] if [[ "${reconfigure}" == true ]]; then echo "::: --reconfigure passed to install script. Not downloading/updating local repos" else # Get Git files for Core and Admin getGitFiles ${PI_HOLE_LOCAL_REPO} ${piholeGitUrl} getGitFiles ${webInterfaceDir} ${webInterfaceGitUrl} fi if [[ ${useUpdateVars} == false ]]; then # Display welcome dialogs welcomeDialogs # Create directory for Pi-hole storage mkdir -p /etc/pihole/ # Remove legacy scripts from previous storage location remove_legacy_scripts # Stop resolver and webserver while installing proceses stop_service dnsmasq stop_service lighttpd # Determine available interfaces get_available_interfaces # Find interfaces and let the user choose one chooseInterface # Let the user decide if they want to block ads over IPv4 and/or IPv6 use4andor6 # Decide what upstream DNS Servers to use setDNS # Let the user decide if they want query logging enabled... setLogging # Install packages used by the Pi-hole install_dependent_packages PIHOLE_DEPS[@] # Install and log everything to a file installPihole | tee ${tmpLog} else # update packages used by the Pi-hole install_dependent_packages PIHOLE_DEPS[@] updatePihole | tee ${tmpLog} fi # Move the log file into /etc/pihole for storage mv ${tmpLog} ${instalLogLoc} # Add password to web UI if there is none pw="" if [[ $(grep 'WEBPASSWORD' -c /etc/pihole/setupVars.conf) == 0 ]] ; then pw=$(tr -dc _A-Z-a-z-0-9 < /dev/urandom | head -c 8) pihole -a -p ${pw} fi if [[ "${useUpdateVars}" == false ]]; then displayFinalMessage ${pw} fi echo "::: Restarting services..." # Start services start_service dnsmasq enable_service dnsmasq start_service lighttpd enable_service lighttpd echo "::: done." echo ":::" if [[ "${useUpdateVars}" == false ]]; then echo "::: Installation Complete! Configure your devices to use the Pi-hole as their DNS server using:" echo "::: ${IPV4_ADDRESS%/*}" echo "::: ${IPV6_ADDRESS}" echo ":::" echo "::: If you set a new IP address, you should restart the Pi." echo "::: View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin" else echo "::: Update complete!" fi if (( ${#pw} > 0 )) ; then echo ":::" echo "::: Note: As security measure a password has been installed for your web interface" echo "::: The currently set password is" echo "::: ${pw}" echo ":::" echo "::: You can always change it using" echo "::: pihole -a -p new_password" fi echo ":::" echo "::: The install log is located at: /etc/pihole/install.log" } if [[ "${PH_TEST}" != true ]] ; then main "$@" fi \ No newline at end of file From 5c43df66a8e94ca08709a322ae8b0dfcb4b9e7e3 Mon Sep 17 00:00:00 2001 From: Dan Schaper Date: Tue, 20 Dec 2016 15:07:36 -0800 Subject: [PATCH 25/89] Remove spinner function, masked return values. Signed-off-by: Dan Schaper --- automated install/basic-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 96c8ece3..d96c7547 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -1 +1 @@ -#!/usr/bin/env bash # Pi-hole: A black hole for Internet advertisements # (c) 2015, 2016 by Jacob Salmela # Network-wide ad blocking via your Raspberry Pi # http://pi-hole.net # Installs Pi-hole # # Pi-hole is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # pi-hole.net/donate # # Install with this command (from your Pi): # # curl -L install.pi-hole.net | bash set -e ######## VARIABLES ######### tmpLog=/tmp/pihole-install.log instalLogLoc=/etc/pihole/install.log setupVars=/etc/pihole/setupVars.conf webInterfaceGitUrl="https://github.com/pi-hole/AdminLTE.git" webInterfaceDir="/var/www/html/admin" piholeGitUrl="https://github.com/pi-hole/pi-hole.git" PI_HOLE_LOCAL_REPO="/etc/.pihole" PI_HOLE_FILES=(chronometer list piholeDebug piholeLogFlush setupLCD update version) useUpdateVars=false IPV4_ADDRESS="" IPV6_ADDRESS="" QUERY_LOGGING=true # Find the rows and columns will default to 80x24 is it can not be detected screen_size=$(stty size 2>/dev/null || echo 24 80) rows=$(echo $screen_size | awk '{print $1}') columns=$(echo $screen_size | awk '{print $2}') # Divide by two so the dialogs take up half of the screen, which looks nice. r=$(( rows / 2 )) c=$(( columns / 2 )) # Unless the screen is tiny r=$(( r < 20 ? 20 : r )) c=$(( c < 70 ? 70 : c )) ######## Undocumented Flags. Shhh ######## skipSpaceCheck=false reconfigure=false runUnattended=false ######## FIRST CHECK ######## # Must be root to install echo ":::" if [[ ${EUID} -eq 0 ]]; then echo "::: You are root." else echo "::: Script called with non-root privileges. The Pi-hole installs server packages and configures" echo "::: system networking, it requires elevated rights. Please check the contents of the script for" echo "::: any concerns with this requirement. Please be sure to download this script from a trusted source." echo ":::" echo "::: Detecting the presence of the sudo utility for continuation of this install..." if [ -x "$(command -v sudo)" ]; then echo "::: Utility sudo located." exec curl -sSL https://install.pi-hole.net | sudo bash "$@" exit $? else echo "::: sudo is needed for the Web interface to run pihole commands. Please run this script as root and it will be automatically installed." exit 1 fi fi # Compatibility if [[ $(command -v apt-get) ]]; then #Debian Family ############################################# PKG_MANAGER="apt-get" PKG_CACHE="/var/lib/apt/lists/" UPDATE_PKG_CACHE="${PKG_MANAGER} update" PKG_UPDATE="${PKG_MANAGER} upgrade" PKG_INSTALL="${PKG_MANAGER} --yes --fix-missing install" # grep -c will return 1 retVal on 0 matches, block this throwing the set -e with an OR TRUE PKG_COUNT="${PKG_MANAGER} -s -o Debug::NoLocking=true upgrade | grep -c ^Inst || true" # ######################################### # fixes for dependancy differences # Debian 7 doesn't have iproute2 use iproute ${PKG_MANAGER} install --dry-run iproute2 > /dev/null 2>&1 && IPROUTE_PKG="iproute2" || IPROUTE_PKG="iproute" # Prefer the php metapackage if it's there, fall back on the php5 pacakges ${PKG_MANAGER} install --dry-run php > /dev/null 2>&1 && phpVer="php" || phpVer="php5" # ######################################### INSTALLER_DEPS=( apt-utils whiptail git dhcpcd5) PIHOLE_DEPS=( iputils-ping lsof dnsutils bc dnsmasq lighttpd ${phpVer}-common ${phpVer}-cgi curl unzip wget sudo netcat cron ${IPROUTE_PKG} ) LIGHTTPD_USER="www-data" LIGHTTPD_GROUP="www-data" LIGHTTPD_CFG="lighttpd.conf.debian" DNSMASQ_USER="dnsmasq" package_check_install() { dpkg-query -W -f='${Status}' "${1}" 2>/dev/null | grep -c "ok installed" || ${PKG_INSTALL} "${1}" } elif [ $(command -v rpm) ]; then # Fedora Family if [ $(command -v dnf) ]; then PKG_MANAGER="dnf" else PKG_MANAGER="yum" fi PKG_CACHE="/var/cache/${PKG_MANAGER}" UPDATE_PKG_CACHE="${PKG_MANAGER} check-update" PKG_UPDATE="${PKG_MANAGER} update -y" PKG_INSTALL="${PKG_MANAGER} install -y" PKG_COUNT="${PKG_MANAGER} check-update | egrep '(.i686|.x86|.noarch|.arm|.src)' | wc -l" INSTALLER_DEPS=( iproute net-tools procps-ng newt git ) PIHOLE_DEPS=( epel-release bind-utils bc dnsmasq lighttpd lighttpd-fastcgi php-common php-cli php curl unzip wget findutils cronie sudo nmap-ncat ) if grep -q 'Fedora' /etc/redhat-release; then remove_deps=(epel-release); PIHOLE_DEPS=( ${PIHOLE_DEPS[@]/$remove_deps} ); fi LIGHTTPD_USER="lighttpd" LIGHTTPD_GROUP="lighttpd" LIGHTTPD_CFG="lighttpd.conf.fedora" DNSMASQ_USER="nobody" package_check_install() { rpm -qa | grep ^"${1}"- > /dev/null || ${PKG_INSTALL} "${1}" } else echo "OS distribution not supported" exit fi ####### FUNCTIONS ########## spinner() { local pid=$1 local delay=0.50 local spinstr='/-\|' while [ "$(ps a | awk '{print $1}' | grep "${pid}")" ]; do local temp=${spinstr#?} printf " [%c] " "${spinstr}" local spinstr=${temp}${spinstr%"$temp"} sleep ${delay} printf "\b\b\b\b\b\b" done printf " \b\b\b\b" } is_repo() { # Use git to check if directory is currently under VCS, return the value local directory="${1}" git -C "${directory}" status --short &> /dev/null return } make_repo() { local directory="${1}" local remoteRepo="${2}" # Remove the non-repod interface and clone the interface echo -n "::: Cloning $remoteRepo into $directory..." rm -rf "${directory}" git clone -q --depth 1 "${remoteRepo}" "${directory}" > /dev/null & spinner $! echo " done!" } update_repo() { local directory="${1}" # Pull the latest commits echo -n "::: Updating repo in $1..." cd "${directory}" || exit 1 git stash -q > /dev/null & spinner $! git pull -q > /dev/null & spinner $! echo " done!" } getGitFiles() { # Setup git repos for directory and repository passed # as arguments 1 and 2 local directory="${1}" local remoteRepo="${2}" echo ":::" echo "::: Checking for existing repository..." if is_repo "${directory}"; then update_repo "${directory}" else make_repo "${directory}" "${remoteRepo}" fi } find_IPv4_information() { # Find IP used to route to outside world IPv4dev=$(ip route get 8.8.8.8 | awk '{for(i=1;i<=NF;i++)if($i~/dev/)print $(i+1)}') IPV4_ADDRESS=$(ip -o -f inet addr show dev "$IPv4dev" | awk '{print $4}' | awk 'END {print}') IPv4gw=$(ip route get 8.8.8.8 | awk '{print $3}') } get_available_interfaces() { # Get available interfaces. Consider only getting UP interfaces in the future, and leaving DOWN interfaces out of list. availableInterfaces=$(ip -o link | awk '{print $2}' | grep -v "lo" | cut -d':' -f1 | cut -d'@' -f1) } welcomeDialogs() { # Display the welcome dialog whiptail --msgbox --backtitle "Welcome" --title "Pi-hole automated installer" "\n\nThis installer will transform your device into a network-wide ad blocker!" ${r} ${c} # Support for a part-time dev whiptail --msgbox --backtitle "Plea" --title "Free and open source" "\n\nThe Pi-hole is free, but powered by your donations: http://pi-hole.net/donate" ${r} ${c} # Explain the need for a static address whiptail --msgbox --backtitle "Initiating network interface" --title "Static IP Needed" "\n\nThe Pi-hole is a SERVER so it needs a STATIC IP ADDRESS to function properly. In the next section, you can choose to use your current network settings (DHCP) or to manually edit them." ${r} ${c} } verifyFreeDiskSpace() { # 50MB is the minimum space needed (45MB install (includes web admin bootstrap/jquery libraries etc) + 5MB one day of logs.) # - Fourdee: Local ensures the variable is only created, and accessible within this function/void. Generally considered a "good" coding practice for non-global variables. echo "::: Verifying free disk space..." local required_free_kilobytes=51200 local existing_free_kilobytes=$(df -Pk | grep -m1 '\/$' | awk '{print $4}') # - Unknown free disk space , not a integer if ! [[ "${existing_free_kilobytes}" =~ ^([0-9])+$ ]]; then echo "::: Unknown free disk space!" echo "::: We were unable to determine available free disk space on this system." echo "::: You may override this check and force the installation, however, it is not recommended" echo "::: To do so, pass the argument '--i_do_not_follow_recommendations' to the install script" echo "::: eg. curl -L https://install.pi-hole.net | bash /dev/stdin --i_do_not_follow_recommendations" exit 1 # - Insufficient free disk space elif [[ ${existing_free_kilobytes} -lt ${required_free_kilobytes} ]]; then echo "::: Insufficient Disk Space!" echo "::: Your system appears to be low on disk space. pi-hole recommends a minimum of $required_free_kilobytes KiloBytes." echo "::: You only have ${existing_free_kilobytes} KiloBytes free." echo "::: If this is a new install you may need to expand your disk." echo "::: Try running 'sudo raspi-config', and choose the 'expand file system option'" echo "::: After rebooting, run this installation again. (curl -L https://install.pi-hole.net | bash)" echo "Insufficient free space, exiting..." exit 1 fi } chooseInterface() { # Turn the available interfaces into an array so it can be used with a whiptail dialog local interfacesArray=() # Number of available interfaces local interfaceCount # Whiptail variable storage local chooseInterfaceCmd # Temporary Whiptail options storage local chooseInterfaceOptions # Loop sentinel variable local firstLoop=1 while read -r line; do mode="OFF" if [[ ${firstLoop} -eq 1 ]]; then firstLoop=0 mode="ON" fi interfacesArray+=("${line}" "available" "${mode}") done <<< "${availableInterfaces}" # Find out how many interfaces are available to choose from interfaceCount=$(echo "${availableInterfaces}" | wc -l) chooseInterfaceCmd=(whiptail --separate-output --radiolist "Choose An Interface (press space to select)" ${r} ${c} ${interfaceCount}) chooseInterfaceOptions=$("${chooseInterfaceCmd[@]}" "${interfacesArray[@]}" 2>&1 >/dev/tty) if [[ $? = 0 ]]; then for desiredInterface in ${chooseInterfaceOptions}; do PIHOLE_INTERFACE=${desiredInterface} echo "::: Using interface: $PIHOLE_INTERFACE" done else echo "::: Cancel selected, exiting...." exit 1 fi } useIPv6dialog() { # Show the IPv6 address used for blocking IPV6_ADDRESS=$(ip -6 route get 2001:4860:4860::8888 | awk -F " " '{ for(i=1;i<=NF;i++) if ($i == "src") print $(i+1) }') whiptail --msgbox --backtitle "IPv6..." --title "IPv6 Supported" "$IPV6_ADDRESS will be used to block ads." ${r} ${c} } use4andor6() { local useIPv4 local useIPv6 # Let use select IPv4 and/or IPv6 cmd=(whiptail --separate-output --checklist "Select Protocols (press space to select)" ${r} ${c} 2) options=(IPv4 "Block ads over IPv4" on IPv6 "Block ads over IPv6" on) choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty) if [[ $? = 0 ]];then for choice in ${choices} do case ${choice} in IPv4 ) useIPv4=true;; IPv6 ) useIPv6=true;; esac done if [[ ${useIPv4} ]]; then find_IPv4_information getStaticIPv4Settings setStaticIPv4 fi if [[ ${useIPv6} ]]; then useIPv6dialog fi echo "::: IPv4 address: ${IPV4_ADDRESS}" echo "::: IPv6 address: ${IPV6_ADDRESS}" if [ ! ${useIPv4} ] && [ ! ${useIPv6} ]; then echo "::: Cannot continue, neither IPv4 or IPv6 selected" echo "::: Exiting" exit 1 fi else echo "::: Cancel selected. Exiting..." exit 1 fi } getStaticIPv4Settings() { # Ask if the user wants to use DHCP settings as their static IP if (whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Do you want to use your current network settings as a static address? IP address: ${IPV4_ADDRESS} Gateway: ${IPv4gw}" ${r} ${c}); then # If they choose yes, let the user know that the IP address will not be available via DHCP and may cause a conflict. whiptail --msgbox --backtitle "IP information" --title "FYI: IP Conflict" "It is possible your router could still try to assign this IP to a device, which would cause a conflict. But in most cases the router is smart enough to not do that. If you are worried, either manually set the address, or modify the DHCP reservation pool so it does not include the IP you want. It is also possible to use a DHCP reservation, but if you are going to do that, you might as well set a static address." ${r} ${c} # Nothing else to do since the variables are already set above else # Otherwise, we need to ask the user to input their desired settings. # Start by getting the IPv4 address (pre-filling it with info gathered from DHCP) # Start a loop to let the user enter their information with the chance to go back and edit it if necessary until [[ ${ipSettingsCorrect} = True ]]; do # Ask for the IPv4 address IPV4_ADDRESS=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 address" --inputbox "Enter your desired IPv4 address" ${r} ${c} "${IPV4_ADDRESS}" 3>&1 1>&2 2>&3) if [[ $? = 0 ]]; then echo "::: Your static IPv4 address: ${IPV4_ADDRESS}" # Ask for the gateway IPv4gw=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 gateway (router)" --inputbox "Enter your desired IPv4 default gateway" ${r} ${c} "${IPv4gw}" 3>&1 1>&2 2>&3) if [[ $? = 0 ]]; then echo "::: Your static IPv4 gateway: ${IPv4gw}" # Give the user a chance to review their settings before moving on if (whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Are these settings correct? IP address: ${IPV4_ADDRESS} Gateway: ${IPv4gw}" ${r} ${c}); then # After that's done, the loop ends and we move on ipSettingsCorrect=True else # If the settings are wrong, the loop continues ipSettingsCorrect=False fi else # Cancelling gateway settings window ipSettingsCorrect=False echo "::: Cancel selected. Exiting..." exit 1 fi else # Cancelling IPv4 settings window ipSettingsCorrect=False echo "::: Cancel selected. Exiting..." exit 1 fi done # End the if statement for DHCP vs. static fi } setDHCPCD() { # Append these lines to dhcpcd.conf to enable a static IP echo "## interface ${PIHOLE_INTERFACE} static ip_address=${IPV4_ADDRESS} static routers=${IPv4gw} static domain_name_servers=${IPv4gw}" | tee -a /etc/dhcpcd.conf >/dev/null } setStaticIPv4() { local IFCFG_FILE local IPADDR local CIDR if [[ -f /etc/dhcpcd.conf ]]; then # Debian Family if grep -q "${IPV4_ADDRESS}" /etc/dhcpcd.conf; then echo "::: Static IP already configured" else setDHCPCD ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" echo ":::" echo "::: Setting IP to ${IPV4_ADDRESS}. You may need to restart after the install is complete." echo ":::" fi elif [[ -f /etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE} ]];then # Fedora Family IFCFG_FILE=/etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE} if grep -q "${IPV4_ADDRESS}" "${IFCFG_FILE}"; then echo "::: Static IP already configured" else IPADDR=$(echo "${IPV4_ADDRESS}" | cut -f1 -d/) CIDR=$(echo "${IPV4_ADDRESS}" | cut -f2 -d/) # Backup existing interface configuration: cp "${IFCFG_FILE}" "${IFCFG_FILE}".pihole.orig # Build Interface configuration file: { echo "# Configured via Pi-Hole installer" echo "DEVICE=$PIHOLE_INTERFACE" echo "BOOTPROTO=none" echo "ONBOOT=yes" echo "IPADDR=$IPADDR" echo "PREFIX=$CIDR" echo "GATEWAY=$IPv4gw" echo "DNS1=$PIHOLE_DNS_1" echo "DNS2=$PIHOLE_DNS_2" echo "USERCTL=no" }> "${IFCFG_FILE}" ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" if [ -x "$(command -v nmcli)" ];then # Tell NetworkManager to read our new sysconfig file nmcli con load "${IFCFG_FILE}" > /dev/null fi echo ":::" echo "::: Setting IP to ${IPV4_ADDRESS}. You may need to restart after the install is complete." echo ":::" fi else echo "::: Warning: Unable to locate configuration file to set static IPv4 address!" exit 1 fi } valid_ip() { local ip=${1} local stat=1 if [[ ${ip} =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then OIFS=$IFS IFS='.' ip=(${ip}) IFS=${OIFS} [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \ && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]] stat=$? fi return ${stat} } setDNS() { DNSChooseCmd=(whiptail --separate-output --radiolist "Select Upstream DNS Provider. To use your own, select Custom." ${r} ${c} 6) DNSChooseOptions=(Google "" on OpenDNS "" off Level3 "" off Norton "" off Comodo "" off Custom "" off) DNSchoices=$("${DNSChooseCmd[@]}" "${DNSChooseOptions[@]}" 2>&1 >/dev/tty) if [[ $? = 0 ]];then case ${DNSchoices} in Google) echo "::: Using Google DNS servers." PIHOLE_DNS_1="8.8.8.8" PIHOLE_DNS_2="8.8.4.4" ;; OpenDNS) echo "::: Using OpenDNS servers." PIHOLE_DNS_1="208.67.222.222" PIHOLE_DNS_2="208.67.220.220" ;; Level3) echo "::: Using Level3 servers." PIHOLE_DNS_1="4.2.2.1" PIHOLE_DNS_2="4.2.2.2" ;; Norton) echo "::: Using Norton ConnectSafe servers." PIHOLE_DNS_1="199.85.126.10" PIHOLE_DNS_2="199.85.127.10" ;; Comodo) echo "::: Using Comodo Secure servers." PIHOLE_DNS_1="8.26.56.26" PIHOLE_DNS_2="8.20.247.20" ;; Custom) until [[ ${DNSSettingsCorrect} = True ]]; do strInvalid="Invalid" if [ ! ${PIHOLE_DNS_1} ]; then if [ ! ${PIHOLE_DNS_2} ]; then prePopulate="" else prePopulate=", ${PIHOLE_DNS_2}" fi elif [ ${PIHOLE_DNS_1} ] && [ ! ${PIHOLE_DNS_2} ]; then prePopulate="${PIHOLE_DNS_1}" elif [ ${PIHOLE_DNS_1} ] && [ ${PIHOLE_DNS_2} ]; then prePopulate="${PIHOLE_DNS_1}, ${PIHOLE_DNS_2}" fi piholeDNS=$(whiptail --backtitle "Specify Upstream DNS Provider(s)" --inputbox "Enter your desired upstream DNS provider(s), seperated by a comma.\n\nFor example '8.8.8.8, 8.8.4.4'" ${r} ${c} "${prePopulate}" 3>&1 1>&2 2>&3) if [[ $? = 0 ]]; then PIHOLE_DNS_1=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$1}') PIHOLE_DNS_2=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$2}') if ! valid_ip "${PIHOLE_DNS_1}" || [ ! "${PIHOLE_DNS_1}" ]; then PIHOLE_DNS_1=${strInvalid} fi if ! valid_ip "${PIHOLE_DNS_2}" && [ "${PIHOLE_DNS_2}" ]; then PIHOLE_DNS_2=${strInvalid} fi else echo "::: Cancel selected, exiting...." exit 1 fi if [[ ${PIHOLE_DNS_1} == "${strInvalid}" ]] || [[ ${PIHOLE_DNS_2} == "${strInvalid}" ]]; then whiptail --msgbox --backtitle "Invalid IP" --title "Invalid IP" "One or both entered IP addresses were invalid. Please try again.\n\n DNS Server 1: $PIHOLE_DNS_1\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c} if [[ ${PIHOLE_DNS_1} == "${strInvalid}" ]]; then PIHOLE_DNS_1="" fi if [[ ${PIHOLE_DNS_2} == "${strInvalid}" ]]; then PIHOLE_DNS_2="" fi DNSSettingsCorrect=False else if (whiptail --backtitle "Specify Upstream DNS Provider(s)" --title "Upstream DNS Provider(s)" --yesno "Are these settings correct?\n DNS Server 1: $PIHOLE_DNS_1\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c}); then DNSSettingsCorrect=True else # If the settings are wrong, the loop continues DNSSettingsCorrect=False fi fi done ;; esac else echo "::: Cancel selected. Exiting..." exit 1 fi } setLogging() { local LogToggleCommand local LogChooseOptions local LogChoices LogToggleCommand=(whiptail --separate-output --radiolist "Do you want to log queries?\n (Disabling will render graphs on the Admin page useless):" ${r} ${c} 6) LogChooseOptions=("On (Reccomended)" "" on Off "" off) LogChoices=$("${LogToggleCommand[@]}" "${LogChooseOptions[@]}" 2>&1 >/dev/tty) || (echo "::: Cancel selected. Exiting..." && exit 1) case ${LogChoices} in "On (Recommended)") echo "::: Logging On." QUERY_LOGGING=true ;; Off) echo "::: Logging Off." QUERY_LOGGING=false ;; esac } version_check_dnsmasq() { # Check if /etc/dnsmasq.conf is from pihole. If so replace with an original and install new in .d directory local dnsmasq_conf="/etc/dnsmasq.conf" local dnsmasq_conf_orig="/etc/dnsmasq.conf.orig" local dnsmasq_pihole_id_string="addn-hosts=/etc/pihole/gravity.list" local dnsmasq_original_config="/etc/.pihole/advanced/dnsmasq.conf.original" local dnsmasq_pihole_01_snippet="/etc/.pihole/advanced/01-pihole.conf" local dnsmasq_pihole_01_location="/etc/dnsmasq.d/01-pihole.conf" if [ -f ${dnsmasq_conf} ]; then echo -n "::: Existing dnsmasq.conf found..." if grep -q ${dnsmasq_pihole_id_string} ${dnsmasq_conf}; then echo " it is from a previous pi-hole install." echo -n "::: Backing up dnsmasq.conf to dnsmasq.conf.orig..." mv -f ${dnsmasq_conf} ${dnsmasq_conf_orig} echo " done." echo -n "::: Restoring default dnsmasq.conf..." cp ${dnsmasq_original_config} ${dnsmasq_conf} echo " done." else echo " it is not a pi-hole file, leaving alone!" fi else echo -n "::: No dnsmasq.conf found.. restoring default dnsmasq.conf..." cp ${dnsmasq_original_config} ${dnsmasq_conf} echo " done." fi echo -n "::: Copying 01-pihole.conf to /etc/dnsmasq.d/01-pihole.conf..." cp ${dnsmasq_pihole_01_snippet} ${dnsmasq_pihole_01_location} echo " done." sed -i "s/@INT@/$PIHOLE_INTERFACE/" ${dnsmasq_pihole_01_location} if [[ "${PIHOLE_DNS_1}" != "" ]]; then sed -i "s/@DNS1@/$PIHOLE_DNS_1/" ${dnsmasq_pihole_01_location} else sed -i '/^server=@DNS1@/d' ${dnsmasq_pihole_01_location} fi if [[ "${PIHOLE_DNS_2}" != "" ]]; then sed -i "s/@DNS2@/$PIHOLE_DNS_2/" ${dnsmasq_pihole_01_location} else sed -i '/^server=@DNS2@/d' ${dnsmasq_pihole_01_location} fi sed -i 's/^#conf-dir=\/etc\/dnsmasq.d$/conf-dir=\/etc\/dnsmasq.d/' ${dnsmasq_conf} if [[ "${QUERY_LOGGING}" == false ]] ; then #Disable Logging sed -i 's/^log-queries/#log-queries/' ${dnsmasq_pihole_01_location} else #Enable Logging sed -i 's/^#log-queries/log-queries/' ${dnsmasq_pihole_01_location} fi } remove_legacy_scripts() { #Tidy up /usr/local/bin directory if installing over previous install. oldFiles=( gravity chronometer whitelist blacklist piholeLogFlush updateDashboard uninstall setupLCD piholeDebug) for i in "${oldFiles[@]}"; do if [ -f "/usr/local/bin/$i.sh" ]; then rm /usr/local/bin/"$i".sh fi done } clean_existing() { # Clean an exiting installation to prepare for upgrade/reinstall # ${1} Directory to clean; ${2} Array of files to remove local clean_directory="${1}" local old_files=${2} for script in "${old_files[@]}"; do rm -f "${clean_directory}${script}.sh" done } installScripts() { # Install the scripts from repository to their various locations readonly install_dir="/opt/pihole/" echo ":::" echo -n "::: Installing scripts from ${PI_HOLE_LOCAL_REPO}..." # Clear out script files from Pi-hole scripts directory. clean_existing "${install_dir}" "${PI_HOLE_FILES}" # Install files from local core repository if is_repo "${PI_HOLE_LOCAL_REPO}"; then cd "${PI_HOLE_LOCAL_REPO}" install -o "${USER}" -Dm755 -t /opt/pihole/ gravity.sh install -o "${USER}" -Dm755 -t /opt/pihole/ ./advanced/Scripts/*.sh install -o "${USER}" -Dm755 -t /opt/pihole/ ./automated\ install/uninstall.sh install -o "${USER}" -Dm755 -t /usr/local/bin/ pihole install -Dm644 ./advanced/bash-completion/pihole /etc/bash_completion.d/pihole echo " done." else echo " *** ERROR: Local repo ${core_repo} not found, exiting." exit 1 fi } installConfigs() { # Install the configs from /etc/.pihole to their various locations echo ":::" echo "::: Installing configs..." version_check_dnsmasq if [ ! -d "/etc/lighttpd" ]; then mkdir /etc/lighttpd chown "${USER}":root /etc/lighttpd elif [ -f "/etc/lighttpd/lighttpd.conf" ]; then mv /etc/lighttpd/lighttpd.conf /etc/lighttpd/lighttpd.conf.orig fi cp /etc/.pihole/advanced/${LIGHTTPD_CFG} /etc/lighttpd/lighttpd.conf mkdir -p /var/run/lighttpd chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/run/lighttpd mkdir -p /var/cache/lighttpd/compress chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/compress mkdir -p /var/cache/lighttpd/uploads chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/uploads } stop_service() { # Stop service passed in as argument. # Can softfail, as process may not be installed when this is called echo ":::" echo -n "::: Stopping ${1} service..." if [ -x "$(command -v systemctl)" ]; then systemctl stop "${1}" &> /dev/null & spinner $! || true else service "${1}" stop &> /dev/null & spinner $! || true fi echo " done." } start_service() { # Start/Restart service passed in as argument # This should not fail, it's an error if it does echo ":::" echo -n "::: Starting ${1} service..." if [ -x "$(command -v systemctl)" ]; then systemctl restart "${1}" &> /dev/null & spinner $! else service "${1}" restart &> /dev/null & spinner $! fi echo " done." } enable_service() { # Enable service so that it will start with next reboot echo ":::" echo -n "::: Enabling ${1} service to start on reboot..." if [ -x "$(command -v systemctl)" ]; then systemctl enable "${1}" &> /dev/null & spinner $! else update-rc.d "${1}" defaults &> /dev/null & spinner $! fi echo " done." } update_pacakge_cache() { #Running apt-get update/upgrade with minimal output can cause some issues with #requiring user input (e.g password for phpmyadmin see #218) #Check to see if apt-get update has already been run today #it needs to have been run at least once on new installs! timestamp=$(stat -c %Y ${PKG_CACHE}) timestampAsDate=$(date -d @"${timestamp}" "+%b %e") today=$(date "+%b %e") if [ ! "${today}" == "${timestampAsDate}" ]; then #update package lists echo ":::" echo -n "::: ${PKG_MANAGER} update has not been run today. Running now..." ${UPDATE_PKG_CACHE} &> /dev/null & spinner $! echo " done!" fi } notify_package_updates_available() { # Let user know if they have outdated packages on their system and # advise them to run a package update at soonest possible. echo ":::" echo -n "::: Checking ${PKG_MANAGER} for upgraded packages...." updatesToInstall=$(eval "${PKG_COUNT}") echo " done!" echo ":::" if [[ ${updatesToInstall} -eq "0" ]]; then echo "::: Your system is up to date! Continuing with Pi-hole installation..." else echo "::: There are ${updatesToInstall} updates available for your system!" echo "::: We recommend you run '${PKG_UPDATE}' after installing Pi-Hole! " echo ":::" fi } install_dependent_packages() { # Install packages passed in via argument array # No spinner - conflicts with set -e declare -a argArray1=("${!1}") for i in "${argArray1[@]}"; do echo -n "::: Checking for $i..." package_check_install "${i}" &> /dev/null echo " installed!" done } CreateLogFile() { # Create logfiles if necessary echo ":::" echo -n "::: Creating log file and changing owner to dnsmasq..." if [ ! -f /var/log/pihole.log ]; then touch /var/log/pihole.log chmod 644 /var/log/pihole.log chown "${DNSMASQ_USER}":root /var/log/pihole.log echo " done!" else echo " already exists!" fi } installPiholeWeb() { # Install the web interface echo ":::" echo "::: Installing pihole custom index page..." if [ -d "/var/www/html/pihole" ]; then if [ -f "/var/www/html/pihole/index.html" ]; then echo "::: Existing index.html detected, not overwriting" else echo -n "::: index.html missing, replacing... " cp /etc/.pihole/advanced/index.html /var/www/html/pihole/ echo " done!" fi if [ -f "/var/www/html/pihole/index.js" ]; then echo "::: Existing index.js detected, not overwriting" else echo -n "::: index.js missing, replacing... " cp /etc/.pihole/advanced/index.js /var/www/html/pihole/ echo " done!" fi else mkdir /var/www/html/pihole if [ -f /var/www/html/index.lighttpd.html ]; then mv /var/www/html/index.lighttpd.html /var/www/html/index.lighttpd.orig else printf "\n:::\tNo default index.lighttpd.html file found... not backing up" fi cp /etc/.pihole/advanced/index.* /var/www/html/pihole/. echo " done!" fi # Install Sudoer file echo ":::" echo -n "::: Installing sudoer file..." mkdir -p /etc/sudoers.d/ cp /etc/.pihole/advanced/pihole.sudo /etc/sudoers.d/pihole chmod 0440 /etc/sudoers.d/pihole echo " done!" } installCron() { # Install the cron job echo ":::" echo -n "::: Installing latest Cron script..." cp /etc/.pihole/advanced/pihole.cron /etc/cron.d/pihole echo " done!" } runGravity() { # Run gravity.sh to build blacklists echo ":::" echo "::: Preparing to run gravity.sh to refresh hosts..." if ls /etc/pihole/list* 1> /dev/null 2>&1; then echo "::: Cleaning up previous install (preserving whitelist/blacklist)" rm /etc/pihole/list.* fi echo "::: Running gravity.sh" /opt/pihole/gravity.sh } create_pihole_user() { # Check if user pihole exists and create if not echo "::: Checking if user 'pihole' exists..." id -u pihole &> /dev/null && echo "::: User 'pihole' already exists" || (echo "::: User 'pihole' doesn't exist. Creating..." && useradd -r -s /usr/sbin/nologin pihole) } configureFirewall() { # Allow HTTP and DNS traffic if [ -x "$(command -v firewall-cmd)" ]; then firewall-cmd --state &> /dev/null && ( echo "::: Configuring firewalld for httpd and dnsmasq.." && firewall-cmd --permanent --add-port=80/tcp && firewall-cmd --permanent --add-port=53/tcp \ && firewall-cmd --permanent --add-port=53/udp && firewall-cmd --reload) || echo "::: FirewallD not enabled" elif [ -x "$(command -v iptables)" ]; then echo "::: Configuring iptables for httpd and dnsmasq.." iptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT iptables -A INPUT -p tcp -m tcp --dport 53 -j ACCEPT iptables -A INPUT -p udp -m udp --dport 53 -j ACCEPT else echo "::: No firewall detected.. skipping firewall configuration." fi } finalExports() { # Update variables in setupVars.conf file if [ -e "${setupVars}" ]; then sed -i.update.bak '/PIHOLE_INTERFACE/d;/IPV4_ADDRESS/d;/IPV6_ADDRESS/d;/PIHOLE_DNS_1/d;/PIHOLE_DNS_2/d;/QUERY_LOGGING/d;' "${setupVars}" fi { echo "PIHOLE_INTERFACE=${PIHOLE_INTERFACE}" echo "IPV4_ADDRESS=${IPV4_ADDRESS}" echo "IPV6_ADDRESS=${IPV6_ADDRESS}" echo "PIHOLE_DNS_1=${PIHOLE_DNS_1}" echo "PIHOLE_DNS_2=${PIHOLE_DNS_2}" echo "QUERY_LOGGING=${QUERY_LOGGING}" }>> "${setupVars}" } installPihole() { # Install base files and web interface create_pihole_user if [ ! -d "/var/www/html" ]; then mkdir -p /var/www/html fi chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/www/html chmod 775 /var/www/html usermod -a -G ${LIGHTTPD_GROUP} pihole if [ -x "$(command -v lighty-enable-mod)" ]; then lighty-enable-mod fastcgi fastcgi-php > /dev/null || true else printf "\n:::\tWarning: 'lighty-enable-mod' utility not found. Please ensure fastcgi is enabled if you experience issues.\n" fi installScripts installConfigs CreateLogFile configureSelinux installPiholeWeb installCron configureFirewall finalExports runGravity } accountForRefactor() { # At some point in the future this list can be pruned, for now we'll need it to ensure updates don't break. # Refactoring of install script has changed the name of a couple of variables. Sort them out here. sed -i 's/piholeInterface/PIHOLE_INTERFACE/g' ${setupVars} sed -i 's/IPv4_address/IPV4_ADDRESS/g' ${setupVars} sed -i 's/IPv4addr/IPV4_ADDRESS/g' ${setupVars} sed -i 's/IPv6_address/IPV6_ADDRESS/g' ${setupVars} sed -i 's/piholeIPv6/IPV6_ADDRESS/g' ${setupVars} sed -i 's/piholeDNS1/PIHOLE_DNS_1/g' ${setupVars} sed -i 's/piholeDNS2/PIHOLE_DNS_2/g' ${setupVars} } updatePihole() { accountForRefactor # Source ${setupVars} for use in the rest of the functions. . ${setupVars} # Install base files and web interface installScripts installConfigs CreateLogFile configureSelinux installPiholeWeb installCron configureFirewall finalExports #re-export setupVars.conf to account for any new vars added in new versions runGravity } configureSelinux() { if [ -x "$(command -v getenforce)" ]; then printf "\n::: SELinux Detected\n" printf ":::\tChecking for SELinux policy development packages..." package_check_install "selinux-policy-devel" > /dev/null echo " installed!" printf ":::\tEnabling httpd server side includes (SSI).. " setsebool -P httpd_ssi_exec on &> /dev/null && echo "Success" || echo "SELinux not enabled" printf "\n:::\tCompiling Pi-Hole SELinux policy..\n" if ! [ -x "$(command -v systemctl)" ]; then sed -i.bak '/systemd/d' /etc/.pihole/advanced/selinux/pihole.te fi checkmodule -M -m -o /etc/pihole/pihole.mod /etc/.pihole/advanced/selinux/pihole.te semodule_package -o /etc/pihole/pihole.pp -m /etc/pihole/pihole.mod semodule -i /etc/pihole/pihole.pp rm -f /etc/pihole/pihole.mod semodule -l | grep pihole &> /dev/null && echo "::: Installed Pi-Hole SELinux policy" || echo "::: Warning: Pi-Hole SELinux policy did not install." fi } displayFinalMessage() { if (( ${#1} > 0 )) ; then # Final completion message to user whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: IPv4: ${IPV4_ADDRESS%/*} IPv6: ${IPV6_ADDRESS} If you set a new IP address, you should restart the Pi. The install log is in /etc/pihole. View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin The currently set password is ${1}" ${r} ${c} else whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: IPv4: ${IPV4_ADDRESS%/*} IPv6: ${IPV6_ADDRESS} If you set a new IP address, you should restart the Pi. The install log is in /etc/pihole. View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin" ${r} ${c} fi } update_dialogs() { # reconfigure if [ "${reconfigure}" = true ]; then opt1a="Repair" opt1b="This will retain existing settings" strAdd="You will remain on the same version" else opt1a="Update" opt1b="This will retain existing settings." strAdd="You will be updated to the latest version." fi opt2a="Reconfigure" opt2b="This will allow you to enter new settings" UpdateCmd=$(whiptail --title "Existing Install Detected!" --menu "\n\nWe have detected an existing install.\n\nPlease choose from the following options: \n($strAdd)" ${r} ${c} 2 \ "${opt1a}" "${opt1b}" \ "${opt2a}" "${opt2b}" 3>&2 2>&1 1>&3) if [[ $? = 0 ]];then case ${UpdateCmd} in ${opt1a}) echo "::: ${opt1a} option selected." useUpdateVars=true ;; ${opt2a}) echo "::: ${opt2a} option selected" useUpdateVars=false ;; esac else echo "::: Cancel selected. Exiting..." exit 1 fi } main() { # Check arguments for the undocumented flags for var in "$@"; do case "$var" in "--reconfigure" ) reconfigure=true;; "--i_do_not_follow_recommendations" ) skipSpaceCheck=false;; "--unattended" ) runUnattended=true;; esac done if [[ -f ${setupVars} ]]; then if [[ "${runUnattended}" == true ]]; then echo "::: --unattended passed to install script, no whiptail dialogs will be displayed" useUpdateVars=true else update_dialogs fi fi # Start the installer # Verify there is enough disk space for the install if [[ "${skipSpaceCheck}" == true ]]; then echo "::: --i_do_not_follow_recommendations passed to script, skipping free disk space verification!" else verifyFreeDiskSpace fi # Update package cache update_pacakge_cache # Notify user of package availability notify_package_updates_available # Install packages used by this installation script install_dependent_packages INSTALLER_DEPS[@] if [[ "${reconfigure}" == true ]]; then echo "::: --reconfigure passed to install script. Not downloading/updating local repos" else # Get Git files for Core and Admin getGitFiles ${PI_HOLE_LOCAL_REPO} ${piholeGitUrl} getGitFiles ${webInterfaceDir} ${webInterfaceGitUrl} fi if [[ ${useUpdateVars} == false ]]; then # Display welcome dialogs welcomeDialogs # Create directory for Pi-hole storage mkdir -p /etc/pihole/ # Remove legacy scripts from previous storage location remove_legacy_scripts # Stop resolver and webserver while installing proceses stop_service dnsmasq stop_service lighttpd # Determine available interfaces get_available_interfaces # Find interfaces and let the user choose one chooseInterface # Let the user decide if they want to block ads over IPv4 and/or IPv6 use4andor6 # Decide what upstream DNS Servers to use setDNS # Let the user decide if they want query logging enabled... setLogging # Install packages used by the Pi-hole install_dependent_packages PIHOLE_DEPS[@] # Install and log everything to a file installPihole | tee ${tmpLog} else # update packages used by the Pi-hole install_dependent_packages PIHOLE_DEPS[@] updatePihole | tee ${tmpLog} fi # Move the log file into /etc/pihole for storage mv ${tmpLog} ${instalLogLoc} # Add password to web UI if there is none pw="" if [[ $(grep 'WEBPASSWORD' -c /etc/pihole/setupVars.conf) == 0 ]] ; then pw=$(tr -dc _A-Z-a-z-0-9 < /dev/urandom | head -c 8) pihole -a -p ${pw} fi if [[ "${useUpdateVars}" == false ]]; then displayFinalMessage ${pw} fi echo "::: Restarting services..." # Start services start_service dnsmasq enable_service dnsmasq start_service lighttpd enable_service lighttpd echo "::: done." echo ":::" if [[ "${useUpdateVars}" == false ]]; then echo "::: Installation Complete! Configure your devices to use the Pi-hole as their DNS server using:" echo "::: ${IPV4_ADDRESS%/*}" echo "::: ${IPV6_ADDRESS}" echo ":::" echo "::: If you set a new IP address, you should restart the Pi." echo "::: View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin" else echo "::: Update complete!" fi if (( ${#pw} > 0 )) ; then echo ":::" echo "::: Note: As security measure a password has been installed for your web interface" echo "::: The currently set password is" echo "::: ${pw}" echo ":::" echo "::: You can always change it using" echo "::: pihole -a -p new_password" fi echo ":::" echo "::: The install log is located at: /etc/pihole/install.log" } if [[ "${PH_TEST}" != true ]] ; then main "$@" fi \ No newline at end of file +#!/usr/bin/env bash # Pi-hole: A black hole for Internet advertisements # (c) 2015, 2016 by Jacob Salmela # Network-wide ad blocking via your Raspberry Pi # http://pi-hole.net # Installs Pi-hole # # Pi-hole is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # pi-hole.net/donate # # Install with this command (from your Pi): # # curl -L install.pi-hole.net | bash set -e ######## VARIABLES ######### tmpLog=/tmp/pihole-install.log instalLogLoc=/etc/pihole/install.log setupVars=/etc/pihole/setupVars.conf webInterfaceGitUrl="https://github.com/pi-hole/AdminLTE.git" webInterfaceDir="/var/www/html/admin" piholeGitUrl="https://github.com/pi-hole/pi-hole.git" PI_HOLE_LOCAL_REPO="/etc/.pihole" PI_HOLE_FILES=(chronometer list piholeDebug piholeLogFlush setupLCD update version) useUpdateVars=false IPV4_ADDRESS="" IPV6_ADDRESS="" QUERY_LOGGING=true # Find the rows and columns will default to 80x24 is it can not be detected screen_size=$(stty size 2>/dev/null || echo 24 80) rows=$(echo $screen_size | awk '{print $1}') columns=$(echo $screen_size | awk '{print $2}') # Divide by two so the dialogs take up half of the screen, which looks nice. r=$(( rows / 2 )) c=$(( columns / 2 )) # Unless the screen is tiny r=$(( r < 20 ? 20 : r )) c=$(( c < 70 ? 70 : c )) ######## Undocumented Flags. Shhh ######## skipSpaceCheck=false reconfigure=false runUnattended=false ######## FIRST CHECK ######## # Must be root to install echo ":::" if [[ ${EUID} -eq 0 ]]; then echo "::: You are root." else echo "::: Script called with non-root privileges. The Pi-hole installs server packages and configures" echo "::: system networking, it requires elevated rights. Please check the contents of the script for" echo "::: any concerns with this requirement. Please be sure to download this script from a trusted source." echo ":::" echo "::: Detecting the presence of the sudo utility for continuation of this install..." if [ -x "$(command -v sudo)" ]; then echo "::: Utility sudo located." exec curl -sSL https://install.pi-hole.net | sudo bash "$@" exit $? else echo "::: sudo is needed for the Web interface to run pihole commands. Please run this script as root and it will be automatically installed." exit 1 fi fi # Compatibility if [[ $(command -v apt-get) ]]; then #Debian Family ############################################# PKG_MANAGER="apt-get" PKG_CACHE="/var/lib/apt/lists/" UPDATE_PKG_CACHE="${PKG_MANAGER} update" PKG_UPDATE="${PKG_MANAGER} upgrade" PKG_INSTALL="${PKG_MANAGER} --yes --fix-missing install" # grep -c will return 1 retVal on 0 matches, block this throwing the set -e with an OR TRUE PKG_COUNT="${PKG_MANAGER} -s -o Debug::NoLocking=true upgrade | grep -c ^Inst || true" # ######################################### # fixes for dependancy differences # Debian 7 doesn't have iproute2 use iproute ${PKG_MANAGER} install --dry-run iproute2 > /dev/null 2>&1 && IPROUTE_PKG="iproute2" || IPROUTE_PKG="iproute" # Prefer the php metapackage if it's there, fall back on the php5 pacakges ${PKG_MANAGER} install --dry-run php > /dev/null 2>&1 && phpVer="php" || phpVer="php5" # ######################################### INSTALLER_DEPS=(apt-utils whiptail git dhcpcd5) PIHOLE_DEPS=(iputils-ping lsof dnsutils bc dnsmasq lighttpd ${phpVer}-common ${phpVer}-cgi curl unzip wget sudo netcat cron ${IPROUTE_PKG}) LIGHTTPD_USER="www-data" LIGHTTPD_GROUP="www-data" LIGHTTPD_CFG="lighttpd.conf.debian" DNSMASQ_USER="dnsmasq" package_check_install() { dpkg-query -W -f='${Status}' "${1}" 2>/dev/null | grep -c "ok installed" || ${PKG_INSTALL} "${1}" } elif [ $(command -v rpm) ]; then # Fedora Family if [ $(command -v dnf) ]; then PKG_MANAGER="dnf" else PKG_MANAGER="yum" fi PKG_CACHE="/var/cache/${PKG_MANAGER}" UPDATE_PKG_CACHE="${PKG_MANAGER} check-update" PKG_UPDATE="${PKG_MANAGER} update -y" PKG_INSTALL="${PKG_MANAGER} install -y" PKG_COUNT="${PKG_MANAGER} check-update | egrep '(.i686|.x86|.noarch|.arm|.src)' | wc -l" INSTALLER_DEPS=(iproute net-tools procps-ng newt git) PIHOLE_DEPS=(epel-release bind-utils bc dnsmasq lighttpd lighttpd-fastcgi php-common php-cli php curl unzip wget findutils cronie sudo nmap-ncat) if grep -q 'Fedora' /etc/redhat-release; then remove_deps=(epel-release); PIHOLE_DEPS=( ${PIHOLE_DEPS[@]/$remove_deps} ); fi LIGHTTPD_USER="lighttpd" LIGHTTPD_GROUP="lighttpd" LIGHTTPD_CFG="lighttpd.conf.fedora" DNSMASQ_USER="nobody" package_check_install() { rpm -qa | grep ^"${1}"- > /dev/null || ${PKG_INSTALL} "${1}" } else echo "OS distribution not supported" exit fi ####### FUNCTIONS ########## is_repo() { # Use git to check if directory is currently under VCS, return the value local directory="${1}" git -C "${directory}" status --short &> /dev/null return } make_repo() { local directory="${1}" local remoteRepo="${2}" # Remove the non-repod interface and clone the interface echo -n "::: Cloning $remoteRepo into $directory..." rm -rf "${directory}" git clone -q --depth 1 "${remoteRepo}" "${directory}" &> /dev/null echo " done!" } update_repo() { local directory="${1}" # Pull the latest commits echo -n "::: Updating repo in $1..." cd "${directory}" || exit 1 git stash -q &> /dev/null git pull -q &> /dev/null echo " done!" } getGitFiles() { # Setup git repos for directory and repository passed # as arguments 1 and 2 local directory="${1}" local remoteRepo="${2}" echo ":::" echo "::: Checking for existing repository..." if is_repo "${directory}"; then update_repo "${directory}" else make_repo "${directory}" "${remoteRepo}" fi } find_IPv4_information() { # Find IP used to route to outside world IPv4dev=$(ip route get 8.8.8.8 | awk '{for(i=1;i<=NF;i++)if($i~/dev/)print $(i+1)}') IPV4_ADDRESS=$(ip -o -f inet addr show dev "$IPv4dev" | awk '{print $4}' | awk 'END {print}') IPv4gw=$(ip route get 8.8.8.8 | awk '{print $3}') } get_available_interfaces() { # Get available interfaces. Consider only getting UP interfaces in the future, and leaving DOWN interfaces out of list. availableInterfaces=$(ip -o link | awk '{print $2}' | grep -v "lo" | cut -d':' -f1 | cut -d'@' -f1) } welcomeDialogs() { # Display the welcome dialog whiptail --msgbox --backtitle "Welcome" --title "Pi-hole automated installer" "\n\nThis installer will transform your device into a network-wide ad blocker!" ${r} ${c} # Support for a part-time dev whiptail --msgbox --backtitle "Plea" --title "Free and open source" "\n\nThe Pi-hole is free, but powered by your donations: http://pi-hole.net/donate" ${r} ${c} # Explain the need for a static address whiptail --msgbox --backtitle "Initiating network interface" --title "Static IP Needed" "\n\nThe Pi-hole is a SERVER so it needs a STATIC IP ADDRESS to function properly. In the next section, you can choose to use your current network settings (DHCP) or to manually edit them." ${r} ${c} } verifyFreeDiskSpace() { # 50MB is the minimum space needed (45MB install (includes web admin bootstrap/jquery libraries etc) + 5MB one day of logs.) # - Fourdee: Local ensures the variable is only created, and accessible within this function/void. Generally considered a "good" coding practice for non-global variables. echo "::: Verifying free disk space..." local required_free_kilobytes=51200 local existing_free_kilobytes=$(df -Pk | grep -m1 '\/$' | awk '{print $4}') # - Unknown free disk space , not a integer if ! [[ "${existing_free_kilobytes}" =~ ^([0-9])+$ ]]; then echo "::: Unknown free disk space!" echo "::: We were unable to determine available free disk space on this system." echo "::: You may override this check and force the installation, however, it is not recommended" echo "::: To do so, pass the argument '--i_do_not_follow_recommendations' to the install script" echo "::: eg. curl -L https://install.pi-hole.net | bash /dev/stdin --i_do_not_follow_recommendations" exit 1 # - Insufficient free disk space elif [[ ${existing_free_kilobytes} -lt ${required_free_kilobytes} ]]; then echo "::: Insufficient Disk Space!" echo "::: Your system appears to be low on disk space. pi-hole recommends a minimum of $required_free_kilobytes KiloBytes." echo "::: You only have ${existing_free_kilobytes} KiloBytes free." echo "::: If this is a new install you may need to expand your disk." echo "::: Try running 'sudo raspi-config', and choose the 'expand file system option'" echo "::: After rebooting, run this installation again. (curl -L https://install.pi-hole.net | bash)" echo "Insufficient free space, exiting..." exit 1 fi } chooseInterface() { # Turn the available interfaces into an array so it can be used with a whiptail dialog local interfacesArray=() # Number of available interfaces local interfaceCount # Whiptail variable storage local chooseInterfaceCmd # Temporary Whiptail options storage local chooseInterfaceOptions # Loop sentinel variable local firstLoop=1 while read -r line; do mode="OFF" if [[ ${firstLoop} -eq 1 ]]; then firstLoop=0 mode="ON" fi interfacesArray+=("${line}" "available" "${mode}") done <<< "${availableInterfaces}" # Find out how many interfaces are available to choose from interfaceCount=$(echo "${availableInterfaces}" | wc -l) chooseInterfaceCmd=(whiptail --separate-output --radiolist "Choose An Interface (press space to select)" ${r} ${c} ${interfaceCount}) chooseInterfaceOptions=$("${chooseInterfaceCmd[@]}" "${interfacesArray[@]}" 2>&1 >/dev/tty) if [[ $? = 0 ]]; then for desiredInterface in ${chooseInterfaceOptions}; do PIHOLE_INTERFACE=${desiredInterface} echo "::: Using interface: $PIHOLE_INTERFACE" done else echo "::: Cancel selected, exiting...." exit 1 fi } useIPv6dialog() { # Show the IPv6 address used for blocking IPV6_ADDRESS=$(ip -6 route get 2001:4860:4860::8888 | awk -F " " '{ for(i=1;i<=NF;i++) if ($i == "src") print $(i+1) }') whiptail --msgbox --backtitle "IPv6..." --title "IPv6 Supported" "$IPV6_ADDRESS will be used to block ads." ${r} ${c} } use4andor6() { local useIPv4 local useIPv6 # Let use select IPv4 and/or IPv6 cmd=(whiptail --separate-output --checklist "Select Protocols (press space to select)" ${r} ${c} 2) options=(IPv4 "Block ads over IPv4" on IPv6 "Block ads over IPv6" on) choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty) if [[ $? = 0 ]];then for choice in ${choices} do case ${choice} in IPv4 ) useIPv4=true;; IPv6 ) useIPv6=true;; esac done if [[ ${useIPv4} ]]; then find_IPv4_information getStaticIPv4Settings setStaticIPv4 fi if [[ ${useIPv6} ]]; then useIPv6dialog fi echo "::: IPv4 address: ${IPV4_ADDRESS}" echo "::: IPv6 address: ${IPV6_ADDRESS}" if [ ! ${useIPv4} ] && [ ! ${useIPv6} ]; then echo "::: Cannot continue, neither IPv4 or IPv6 selected" echo "::: Exiting" exit 1 fi else echo "::: Cancel selected. Exiting..." exit 1 fi } getStaticIPv4Settings() { # Ask if the user wants to use DHCP settings as their static IP if (whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Do you want to use your current network settings as a static address? IP address: ${IPV4_ADDRESS} Gateway: ${IPv4gw}" ${r} ${c}); then # If they choose yes, let the user know that the IP address will not be available via DHCP and may cause a conflict. whiptail --msgbox --backtitle "IP information" --title "FYI: IP Conflict" "It is possible your router could still try to assign this IP to a device, which would cause a conflict. But in most cases the router is smart enough to not do that. If you are worried, either manually set the address, or modify the DHCP reservation pool so it does not include the IP you want. It is also possible to use a DHCP reservation, but if you are going to do that, you might as well set a static address." ${r} ${c} # Nothing else to do since the variables are already set above else # Otherwise, we need to ask the user to input their desired settings. # Start by getting the IPv4 address (pre-filling it with info gathered from DHCP) # Start a loop to let the user enter their information with the chance to go back and edit it if necessary until [[ ${ipSettingsCorrect} = True ]]; do # Ask for the IPv4 address IPV4_ADDRESS=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 address" --inputbox "Enter your desired IPv4 address" ${r} ${c} "${IPV4_ADDRESS}" 3>&1 1>&2 2>&3) if [[ $? = 0 ]]; then echo "::: Your static IPv4 address: ${IPV4_ADDRESS}" # Ask for the gateway IPv4gw=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 gateway (router)" --inputbox "Enter your desired IPv4 default gateway" ${r} ${c} "${IPv4gw}" 3>&1 1>&2 2>&3) if [[ $? = 0 ]]; then echo "::: Your static IPv4 gateway: ${IPv4gw}" # Give the user a chance to review their settings before moving on if (whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Are these settings correct? IP address: ${IPV4_ADDRESS} Gateway: ${IPv4gw}" ${r} ${c}); then # After that's done, the loop ends and we move on ipSettingsCorrect=True else # If the settings are wrong, the loop continues ipSettingsCorrect=False fi else # Cancelling gateway settings window ipSettingsCorrect=False echo "::: Cancel selected. Exiting..." exit 1 fi else # Cancelling IPv4 settings window ipSettingsCorrect=False echo "::: Cancel selected. Exiting..." exit 1 fi done # End the if statement for DHCP vs. static fi } setDHCPCD() { # Append these lines to dhcpcd.conf to enable a static IP echo "## interface ${PIHOLE_INTERFACE} static ip_address=${IPV4_ADDRESS} static routers=${IPv4gw} static domain_name_servers=${IPv4gw}" | tee -a /etc/dhcpcd.conf >/dev/null } setStaticIPv4() { local IFCFG_FILE local IPADDR local CIDR if [[ -f /etc/dhcpcd.conf ]]; then # Debian Family if grep -q "${IPV4_ADDRESS}" /etc/dhcpcd.conf; then echo "::: Static IP already configured" else setDHCPCD ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" echo ":::" echo "::: Setting IP to ${IPV4_ADDRESS}. You may need to restart after the install is complete." echo ":::" fi elif [[ -f /etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE} ]];then # Fedora Family IFCFG_FILE=/etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE} if grep -q "${IPV4_ADDRESS}" "${IFCFG_FILE}"; then echo "::: Static IP already configured" else IPADDR=$(echo "${IPV4_ADDRESS}" | cut -f1 -d/) CIDR=$(echo "${IPV4_ADDRESS}" | cut -f2 -d/) # Backup existing interface configuration: cp "${IFCFG_FILE}" "${IFCFG_FILE}".pihole.orig # Build Interface configuration file: { echo "# Configured via Pi-Hole installer" echo "DEVICE=$PIHOLE_INTERFACE" echo "BOOTPROTO=none" echo "ONBOOT=yes" echo "IPADDR=$IPADDR" echo "PREFIX=$CIDR" echo "GATEWAY=$IPv4gw" echo "DNS1=$PIHOLE_DNS_1" echo "DNS2=$PIHOLE_DNS_2" echo "USERCTL=no" }> "${IFCFG_FILE}" ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" if [ -x "$(command -v nmcli)" ];then # Tell NetworkManager to read our new sysconfig file nmcli con load "${IFCFG_FILE}" > /dev/null fi echo ":::" echo "::: Setting IP to ${IPV4_ADDRESS}. You may need to restart after the install is complete." echo ":::" fi else echo "::: Warning: Unable to locate configuration file to set static IPv4 address!" exit 1 fi } valid_ip() { local ip=${1} local stat=1 if [[ ${ip} =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then OIFS=$IFS IFS='.' ip=(${ip}) IFS=${OIFS} [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \ && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]] stat=$? fi return ${stat} } setDNS() { DNSChooseCmd=(whiptail --separate-output --radiolist "Select Upstream DNS Provider. To use your own, select Custom." ${r} ${c} 6) DNSChooseOptions=(Google "" on OpenDNS "" off Level3 "" off Norton "" off Comodo "" off Custom "" off) DNSchoices=$("${DNSChooseCmd[@]}" "${DNSChooseOptions[@]}" 2>&1 >/dev/tty) if [[ $? = 0 ]];then case ${DNSchoices} in Google) echo "::: Using Google DNS servers." PIHOLE_DNS_1="8.8.8.8" PIHOLE_DNS_2="8.8.4.4" ;; OpenDNS) echo "::: Using OpenDNS servers." PIHOLE_DNS_1="208.67.222.222" PIHOLE_DNS_2="208.67.220.220" ;; Level3) echo "::: Using Level3 servers." PIHOLE_DNS_1="4.2.2.1" PIHOLE_DNS_2="4.2.2.2" ;; Norton) echo "::: Using Norton ConnectSafe servers." PIHOLE_DNS_1="199.85.126.10" PIHOLE_DNS_2="199.85.127.10" ;; Comodo) echo "::: Using Comodo Secure servers." PIHOLE_DNS_1="8.26.56.26" PIHOLE_DNS_2="8.20.247.20" ;; Custom) until [[ ${DNSSettingsCorrect} = True ]]; do strInvalid="Invalid" if [ ! ${PIHOLE_DNS_1} ]; then if [ ! ${PIHOLE_DNS_2} ]; then prePopulate="" else prePopulate=", ${PIHOLE_DNS_2}" fi elif [ ${PIHOLE_DNS_1} ] && [ ! ${PIHOLE_DNS_2} ]; then prePopulate="${PIHOLE_DNS_1}" elif [ ${PIHOLE_DNS_1} ] && [ ${PIHOLE_DNS_2} ]; then prePopulate="${PIHOLE_DNS_1}, ${PIHOLE_DNS_2}" fi piholeDNS=$(whiptail --backtitle "Specify Upstream DNS Provider(s)" --inputbox "Enter your desired upstream DNS provider(s), seperated by a comma.\n\nFor example '8.8.8.8, 8.8.4.4'" ${r} ${c} "${prePopulate}" 3>&1 1>&2 2>&3) if [[ $? = 0 ]]; then PIHOLE_DNS_1=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$1}') PIHOLE_DNS_2=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$2}') if ! valid_ip "${PIHOLE_DNS_1}" || [ ! "${PIHOLE_DNS_1}" ]; then PIHOLE_DNS_1=${strInvalid} fi if ! valid_ip "${PIHOLE_DNS_2}" && [ "${PIHOLE_DNS_2}" ]; then PIHOLE_DNS_2=${strInvalid} fi else echo "::: Cancel selected, exiting...." exit 1 fi if [[ ${PIHOLE_DNS_1} == "${strInvalid}" ]] || [[ ${PIHOLE_DNS_2} == "${strInvalid}" ]]; then whiptail --msgbox --backtitle "Invalid IP" --title "Invalid IP" "One or both entered IP addresses were invalid. Please try again.\n\n DNS Server 1: $PIHOLE_DNS_1\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c} if [[ ${PIHOLE_DNS_1} == "${strInvalid}" ]]; then PIHOLE_DNS_1="" fi if [[ ${PIHOLE_DNS_2} == "${strInvalid}" ]]; then PIHOLE_DNS_2="" fi DNSSettingsCorrect=False else if (whiptail --backtitle "Specify Upstream DNS Provider(s)" --title "Upstream DNS Provider(s)" --yesno "Are these settings correct?\n DNS Server 1: $PIHOLE_DNS_1\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c}); then DNSSettingsCorrect=True else # If the settings are wrong, the loop continues DNSSettingsCorrect=False fi fi done ;; esac else echo "::: Cancel selected. Exiting..." exit 1 fi } setLogging() { local LogToggleCommand local LogChooseOptions local LogChoices LogToggleCommand=(whiptail --separate-output --radiolist "Do you want to log queries?\n (Disabling will render graphs on the Admin page useless):" ${r} ${c} 6) LogChooseOptions=("On (Reccomended)" "" on Off "" off) LogChoices=$("${LogToggleCommand[@]}" "${LogChooseOptions[@]}" 2>&1 >/dev/tty) || (echo "::: Cancel selected. Exiting..." && exit 1) case ${LogChoices} in "On (Recommended)") echo "::: Logging On." QUERY_LOGGING=true ;; Off) echo "::: Logging Off." QUERY_LOGGING=false ;; esac } version_check_dnsmasq() { # Check if /etc/dnsmasq.conf is from pihole. If so replace with an original and install new in .d directory local dnsmasq_conf="/etc/dnsmasq.conf" local dnsmasq_conf_orig="/etc/dnsmasq.conf.orig" local dnsmasq_pihole_id_string="addn-hosts=/etc/pihole/gravity.list" local dnsmasq_original_config="/etc/.pihole/advanced/dnsmasq.conf.original" local dnsmasq_pihole_01_snippet="/etc/.pihole/advanced/01-pihole.conf" local dnsmasq_pihole_01_location="/etc/dnsmasq.d/01-pihole.conf" if [ -f ${dnsmasq_conf} ]; then echo -n "::: Existing dnsmasq.conf found..." if grep -q ${dnsmasq_pihole_id_string} ${dnsmasq_conf}; then echo " it is from a previous pi-hole install." echo -n "::: Backing up dnsmasq.conf to dnsmasq.conf.orig..." mv -f ${dnsmasq_conf} ${dnsmasq_conf_orig} echo " done." echo -n "::: Restoring default dnsmasq.conf..." cp ${dnsmasq_original_config} ${dnsmasq_conf} echo " done." else echo " it is not a pi-hole file, leaving alone!" fi else echo -n "::: No dnsmasq.conf found.. restoring default dnsmasq.conf..." cp ${dnsmasq_original_config} ${dnsmasq_conf} echo " done." fi echo -n "::: Copying 01-pihole.conf to /etc/dnsmasq.d/01-pihole.conf..." cp ${dnsmasq_pihole_01_snippet} ${dnsmasq_pihole_01_location} echo " done." sed -i "s/@INT@/$PIHOLE_INTERFACE/" ${dnsmasq_pihole_01_location} if [[ "${PIHOLE_DNS_1}" != "" ]]; then sed -i "s/@DNS1@/$PIHOLE_DNS_1/" ${dnsmasq_pihole_01_location} else sed -i '/^server=@DNS1@/d' ${dnsmasq_pihole_01_location} fi if [[ "${PIHOLE_DNS_2}" != "" ]]; then sed -i "s/@DNS2@/$PIHOLE_DNS_2/" ${dnsmasq_pihole_01_location} else sed -i '/^server=@DNS2@/d' ${dnsmasq_pihole_01_location} fi sed -i 's/^#conf-dir=\/etc\/dnsmasq.d$/conf-dir=\/etc\/dnsmasq.d/' ${dnsmasq_conf} if [[ "${QUERY_LOGGING}" == false ]] ; then #Disable Logging sed -i 's/^log-queries/#log-queries/' ${dnsmasq_pihole_01_location} else #Enable Logging sed -i 's/^#log-queries/log-queries/' ${dnsmasq_pihole_01_location} fi } remove_legacy_scripts() { #Tidy up /usr/local/bin directory if installing over previous install. oldFiles=( gravity chronometer whitelist blacklist piholeLogFlush updateDashboard uninstall setupLCD piholeDebug) for i in "${oldFiles[@]}"; do if [ -f "/usr/local/bin/$i.sh" ]; then rm /usr/local/bin/"$i".sh fi done } clean_existing() { # Clean an exiting installation to prepare for upgrade/reinstall # ${1} Directory to clean; ${2} Array of files to remove local clean_directory="${1}" local old_files=${2} for script in "${old_files[@]}"; do rm -f "${clean_directory}${script}.sh" done } installScripts() { # Install the scripts from repository to their various locations readonly install_dir="/opt/pihole/" echo ":::" echo -n "::: Installing scripts from ${PI_HOLE_LOCAL_REPO}..." # Clear out script files from Pi-hole scripts directory. clean_existing "${install_dir}" "${PI_HOLE_FILES}" # Install files from local core repository if is_repo "${PI_HOLE_LOCAL_REPO}"; then cd "${PI_HOLE_LOCAL_REPO}" install -o "${USER}" -Dm755 -t /opt/pihole/ gravity.sh install -o "${USER}" -Dm755 -t /opt/pihole/ ./advanced/Scripts/*.sh install -o "${USER}" -Dm755 -t /opt/pihole/ ./automated\ install/uninstall.sh install -o "${USER}" -Dm755 -t /usr/local/bin/ pihole install -Dm644 ./advanced/bash-completion/pihole /etc/bash_completion.d/pihole echo " done." else echo " *** ERROR: Local repo ${core_repo} not found, exiting." exit 1 fi } installConfigs() { # Install the configs from /etc/.pihole to their various locations echo ":::" echo "::: Installing configs..." version_check_dnsmasq if [ ! -d "/etc/lighttpd" ]; then mkdir /etc/lighttpd chown "${USER}":root /etc/lighttpd elif [ -f "/etc/lighttpd/lighttpd.conf" ]; then mv /etc/lighttpd/lighttpd.conf /etc/lighttpd/lighttpd.conf.orig fi cp /etc/.pihole/advanced/${LIGHTTPD_CFG} /etc/lighttpd/lighttpd.conf mkdir -p /var/run/lighttpd chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/run/lighttpd mkdir -p /var/cache/lighttpd/compress chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/compress mkdir -p /var/cache/lighttpd/uploads chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/uploads } stop_service() { # Stop service passed in as argument. # Can softfail, as process may not be installed when this is called echo ":::" echo -n "::: Stopping ${1} service..." if [ -x "$(command -v systemctl)" ]; then systemctl stop "${1}" &> /dev/null || true else service "${1}" stop &> /dev/null || true fi echo " done." } start_service() { # Start/Restart service passed in as argument # This should not fail, it's an error if it does echo ":::" echo -n "::: Starting ${1} service..." if [ -x "$(command -v systemctl)" ]; then systemctl restart "${1}" &> /dev/null else service "${1}" restart &> /dev/null fi echo " done." } enable_service() { # Enable service so that it will start with next reboot echo ":::" echo -n "::: Enabling ${1} service to start on reboot..." if [ -x "$(command -v systemctl)" ]; then systemctl enable "${1}" &> /dev/null else update-rc.d "${1}" defaults &> /dev/null fi echo " done." } update_pacakge_cache() { #Running apt-get update/upgrade with minimal output can cause some issues with #requiring user input (e.g password for phpmyadmin see #218) #Check to see if apt-get update has already been run today #it needs to have been run at least once on new installs! timestamp=$(stat -c %Y ${PKG_CACHE}) timestampAsDate=$(date -d @"${timestamp}" "+%b %e") today=$(date "+%b %e") if [ ! "${today}" == "${timestampAsDate}" ]; then #update package lists echo ":::" echo -n "::: ${PKG_MANAGER} update has not been run today. Running now..." ${UPDATE_PKG_CACHE} &> /dev/null echo " done!" fi } notify_package_updates_available() { # Let user know if they have outdated packages on their system and # advise them to run a package update at soonest possible. echo ":::" echo -n "::: Checking ${PKG_MANAGER} for upgraded packages...." updatesToInstall=$(eval "${PKG_COUNT}") echo " done!" echo ":::" if [[ ${updatesToInstall} -eq "0" ]]; then echo "::: Your system is up to date! Continuing with Pi-hole installation..." else echo "::: There are ${updatesToInstall} updates available for your system!" echo "::: We recommend you run '${PKG_UPDATE}' after installing Pi-Hole! " echo ":::" fi } install_dependent_packages() { # Install packages passed in via argument array # No spinner - conflicts with set -e declare -a argArray1=("${!1}") for i in "${argArray1[@]}"; do echo -n "::: Checking for $i..." package_check_install "${i}" &> /dev/null echo " installed!" done } CreateLogFile() { # Create logfiles if necessary echo ":::" echo -n "::: Creating log file and changing owner to dnsmasq..." if [ ! -f /var/log/pihole.log ]; then touch /var/log/pihole.log chmod 644 /var/log/pihole.log chown "${DNSMASQ_USER}":root /var/log/pihole.log echo " done!" else echo " already exists!" fi } installPiholeWeb() { # Install the web interface echo ":::" echo "::: Installing pihole custom index page..." if [ -d "/var/www/html/pihole" ]; then if [ -f "/var/www/html/pihole/index.html" ]; then echo "::: Existing index.html detected, not overwriting" else echo -n "::: index.html missing, replacing... " cp /etc/.pihole/advanced/index.html /var/www/html/pihole/ echo " done!" fi if [ -f "/var/www/html/pihole/index.js" ]; then echo "::: Existing index.js detected, not overwriting" else echo -n "::: index.js missing, replacing... " cp /etc/.pihole/advanced/index.js /var/www/html/pihole/ echo " done!" fi else mkdir /var/www/html/pihole if [ -f /var/www/html/index.lighttpd.html ]; then mv /var/www/html/index.lighttpd.html /var/www/html/index.lighttpd.orig else printf "\n:::\tNo default index.lighttpd.html file found... not backing up" fi cp /etc/.pihole/advanced/index.* /var/www/html/pihole/. echo " done!" fi # Install Sudoer file echo ":::" echo -n "::: Installing sudoer file..." mkdir -p /etc/sudoers.d/ cp /etc/.pihole/advanced/pihole.sudo /etc/sudoers.d/pihole chmod 0440 /etc/sudoers.d/pihole echo " done!" } installCron() { # Install the cron job echo ":::" echo -n "::: Installing latest Cron script..." cp /etc/.pihole/advanced/pihole.cron /etc/cron.d/pihole echo " done!" } runGravity() { # Run gravity.sh to build blacklists echo ":::" echo "::: Preparing to run gravity.sh to refresh hosts..." if ls /etc/pihole/list* 1> /dev/null 2>&1; then echo "::: Cleaning up previous install (preserving whitelist/blacklist)" rm /etc/pihole/list.* fi echo "::: Running gravity.sh" /opt/pihole/gravity.sh } create_pihole_user() { # Check if user pihole exists and create if not echo "::: Checking if user 'pihole' exists..." id -u pihole &> /dev/null && echo "::: User 'pihole' already exists" || (echo "::: User 'pihole' doesn't exist. Creating..." && useradd -r -s /usr/sbin/nologin pihole) } configureFirewall() { # Allow HTTP and DNS traffic if [ -x "$(command -v firewall-cmd)" ]; then firewall-cmd --state &> /dev/null && ( echo "::: Configuring firewalld for httpd and dnsmasq.." && firewall-cmd --permanent --add-port=80/tcp && firewall-cmd --permanent --add-port=53/tcp \ && firewall-cmd --permanent --add-port=53/udp && firewall-cmd --reload) || echo "::: FirewallD not enabled" elif [ -x "$(command -v iptables)" ]; then echo "::: Configuring iptables for httpd and dnsmasq.." iptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT iptables -A INPUT -p tcp -m tcp --dport 53 -j ACCEPT iptables -A INPUT -p udp -m udp --dport 53 -j ACCEPT else echo "::: No firewall detected.. skipping firewall configuration." fi } finalExports() { # Update variables in setupVars.conf file if [ -e "${setupVars}" ]; then sed -i.update.bak '/PIHOLE_INTERFACE/d;/IPV4_ADDRESS/d;/IPV6_ADDRESS/d;/PIHOLE_DNS_1/d;/PIHOLE_DNS_2/d;/QUERY_LOGGING/d;' "${setupVars}" fi { echo "PIHOLE_INTERFACE=${PIHOLE_INTERFACE}" echo "IPV4_ADDRESS=${IPV4_ADDRESS}" echo "IPV6_ADDRESS=${IPV6_ADDRESS}" echo "PIHOLE_DNS_1=${PIHOLE_DNS_1}" echo "PIHOLE_DNS_2=${PIHOLE_DNS_2}" echo "QUERY_LOGGING=${QUERY_LOGGING}" }>> "${setupVars}" } installPihole() { # Install base files and web interface create_pihole_user if [ ! -d "/var/www/html" ]; then mkdir -p /var/www/html fi chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/www/html chmod 775 /var/www/html usermod -a -G ${LIGHTTPD_GROUP} pihole if [ -x "$(command -v lighty-enable-mod)" ]; then lighty-enable-mod fastcgi fastcgi-php > /dev/null || true else printf "\n:::\tWarning: 'lighty-enable-mod' utility not found. Please ensure fastcgi is enabled if you experience issues.\n" fi installScripts installConfigs CreateLogFile configureSelinux installPiholeWeb installCron configureFirewall finalExports runGravity } accountForRefactor() { # At some point in the future this list can be pruned, for now we'll need it to ensure updates don't break. # Refactoring of install script has changed the name of a couple of variables. Sort them out here. sed -i 's/piholeInterface/PIHOLE_INTERFACE/g' ${setupVars} sed -i 's/IPv4_address/IPV4_ADDRESS/g' ${setupVars} sed -i 's/IPv4addr/IPV4_ADDRESS/g' ${setupVars} sed -i 's/IPv6_address/IPV6_ADDRESS/g' ${setupVars} sed -i 's/piholeIPv6/IPV6_ADDRESS/g' ${setupVars} sed -i 's/piholeDNS1/PIHOLE_DNS_1/g' ${setupVars} sed -i 's/piholeDNS2/PIHOLE_DNS_2/g' ${setupVars} } updatePihole() { accountForRefactor # Source ${setupVars} for use in the rest of the functions. . ${setupVars} # Install base files and web interface installScripts installConfigs CreateLogFile configureSelinux installPiholeWeb installCron configureFirewall finalExports #re-export setupVars.conf to account for any new vars added in new versions runGravity } configureSelinux() { if [ -x "$(command -v getenforce)" ]; then printf "\n::: SELinux Detected\n" printf ":::\tChecking for SELinux policy development packages..." package_check_install "selinux-policy-devel" > /dev/null echo " installed!" printf ":::\tEnabling httpd server side includes (SSI).. " setsebool -P httpd_ssi_exec on &> /dev/null && echo "Success" || echo "SELinux not enabled" printf "\n:::\tCompiling Pi-Hole SELinux policy..\n" if ! [ -x "$(command -v systemctl)" ]; then sed -i.bak '/systemd/d' /etc/.pihole/advanced/selinux/pihole.te fi checkmodule -M -m -o /etc/pihole/pihole.mod /etc/.pihole/advanced/selinux/pihole.te semodule_package -o /etc/pihole/pihole.pp -m /etc/pihole/pihole.mod semodule -i /etc/pihole/pihole.pp rm -f /etc/pihole/pihole.mod semodule -l | grep pihole &> /dev/null && echo "::: Installed Pi-Hole SELinux policy" || echo "::: Warning: Pi-Hole SELinux policy did not install." fi } displayFinalMessage() { if (( ${#1} > 0 )) ; then # Final completion message to user whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: IPv4: ${IPV4_ADDRESS%/*} IPv6: ${IPV6_ADDRESS} If you set a new IP address, you should restart the Pi. The install log is in /etc/pihole. View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin The currently set password is ${1}" ${r} ${c} else whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: IPv4: ${IPV4_ADDRESS%/*} IPv6: ${IPV6_ADDRESS} If you set a new IP address, you should restart the Pi. The install log is in /etc/pihole. View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin" ${r} ${c} fi } update_dialogs() { # reconfigure if [ "${reconfigure}" = true ]; then opt1a="Repair" opt1b="This will retain existing settings" strAdd="You will remain on the same version" else opt1a="Update" opt1b="This will retain existing settings." strAdd="You will be updated to the latest version." fi opt2a="Reconfigure" opt2b="This will allow you to enter new settings" UpdateCmd=$(whiptail --title "Existing Install Detected!" --menu "\n\nWe have detected an existing install.\n\nPlease choose from the following options: \n($strAdd)" ${r} ${c} 2 \ "${opt1a}" "${opt1b}" \ "${opt2a}" "${opt2b}" 3>&2 2>&1 1>&3) if [[ $? = 0 ]];then case ${UpdateCmd} in ${opt1a}) echo "::: ${opt1a} option selected." useUpdateVars=true ;; ${opt2a}) echo "::: ${opt2a} option selected" useUpdateVars=false ;; esac else echo "::: Cancel selected. Exiting..." exit 1 fi } main() { # Check arguments for the undocumented flags for var in "$@"; do case "$var" in "--reconfigure" ) reconfigure=true;; "--i_do_not_follow_recommendations" ) skipSpaceCheck=false;; "--unattended" ) runUnattended=true;; esac done if [[ -f ${setupVars} ]]; then if [[ "${runUnattended}" == true ]]; then echo "::: --unattended passed to install script, no whiptail dialogs will be displayed" useUpdateVars=true else update_dialogs fi fi # Start the installer # Verify there is enough disk space for the install if [[ "${skipSpaceCheck}" == true ]]; then echo "::: --i_do_not_follow_recommendations passed to script, skipping free disk space verification!" else verifyFreeDiskSpace fi # Update package cache update_pacakge_cache # Notify user of package availability notify_package_updates_available # Install packages used by this installation script install_dependent_packages INSTALLER_DEPS[@] if [[ "${reconfigure}" == true ]]; then echo "::: --reconfigure passed to install script. Not downloading/updating local repos" else # Get Git files for Core and Admin getGitFiles ${PI_HOLE_LOCAL_REPO} ${piholeGitUrl} getGitFiles ${webInterfaceDir} ${webInterfaceGitUrl} fi if [[ ${useUpdateVars} == false ]]; then # Display welcome dialogs welcomeDialogs # Create directory for Pi-hole storage mkdir -p /etc/pihole/ # Remove legacy scripts from previous storage location remove_legacy_scripts # Stop resolver and webserver while installing proceses stop_service dnsmasq stop_service lighttpd # Determine available interfaces get_available_interfaces # Find interfaces and let the user choose one chooseInterface # Let the user decide if they want to block ads over IPv4 and/or IPv6 use4andor6 # Decide what upstream DNS Servers to use setDNS # Let the user decide if they want query logging enabled... setLogging # Install packages used by the Pi-hole install_dependent_packages PIHOLE_DEPS[@] # Install and log everything to a file installPihole | tee ${tmpLog} else # update packages used by the Pi-hole install_dependent_packages PIHOLE_DEPS[@] updatePihole | tee ${tmpLog} fi # Move the log file into /etc/pihole for storage mv ${tmpLog} ${instalLogLoc} # Add password to web UI if there is none pw="" if [[ $(grep 'WEBPASSWORD' -c /etc/pihole/setupVars.conf) == 0 ]] ; then pw=$(tr -dc _A-Z-a-z-0-9 < /dev/urandom | head -c 8) pihole -a -p ${pw} fi if [[ "${useUpdateVars}" == false ]]; then displayFinalMessage ${pw} fi echo "::: Restarting services..." # Start services start_service dnsmasq enable_service dnsmasq start_service lighttpd enable_service lighttpd echo "::: done." echo ":::" if [[ "${useUpdateVars}" == false ]]; then echo "::: Installation Complete! Configure your devices to use the Pi-hole as their DNS server using:" echo "::: ${IPV4_ADDRESS%/*}" echo "::: ${IPV6_ADDRESS}" echo ":::" echo "::: If you set a new IP address, you should restart the Pi." echo "::: View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin" else echo "::: Update complete!" fi if (( ${#pw} > 0 )) ; then echo ":::" echo "::: Note: As security measure a password has been installed for your web interface" echo "::: The currently set password is" echo "::: ${pw}" echo ":::" echo "::: You can always change it using" echo "::: pihole -a -p new_password" fi echo ":::" echo "::: The install log is located at: /etc/pihole/install.log" } if [[ "${PH_TEST}" != true ]] ; then main "$@" fi \ No newline at end of file From 9a626948f8b0b72578a562d16bbbc84d13ea2466 Mon Sep 17 00:00:00 2001 From: Dan Schaper Date: Tue, 20 Dec 2016 15:39:32 -0800 Subject: [PATCH 26/89] Use debconf for Deb/Ubu package installations. Signed-off-by: Dan Schaper Alphabetic sort of dependency names Signed-off-by: Dan Schaper --- automated install/basic-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index d96c7547..c64c3864 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -1 +1 @@ -#!/usr/bin/env bash # Pi-hole: A black hole for Internet advertisements # (c) 2015, 2016 by Jacob Salmela # Network-wide ad blocking via your Raspberry Pi # http://pi-hole.net # Installs Pi-hole # # Pi-hole is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # pi-hole.net/donate # # Install with this command (from your Pi): # # curl -L install.pi-hole.net | bash set -e ######## VARIABLES ######### tmpLog=/tmp/pihole-install.log instalLogLoc=/etc/pihole/install.log setupVars=/etc/pihole/setupVars.conf webInterfaceGitUrl="https://github.com/pi-hole/AdminLTE.git" webInterfaceDir="/var/www/html/admin" piholeGitUrl="https://github.com/pi-hole/pi-hole.git" PI_HOLE_LOCAL_REPO="/etc/.pihole" PI_HOLE_FILES=(chronometer list piholeDebug piholeLogFlush setupLCD update version) useUpdateVars=false IPV4_ADDRESS="" IPV6_ADDRESS="" QUERY_LOGGING=true # Find the rows and columns will default to 80x24 is it can not be detected screen_size=$(stty size 2>/dev/null || echo 24 80) rows=$(echo $screen_size | awk '{print $1}') columns=$(echo $screen_size | awk '{print $2}') # Divide by two so the dialogs take up half of the screen, which looks nice. r=$(( rows / 2 )) c=$(( columns / 2 )) # Unless the screen is tiny r=$(( r < 20 ? 20 : r )) c=$(( c < 70 ? 70 : c )) ######## Undocumented Flags. Shhh ######## skipSpaceCheck=false reconfigure=false runUnattended=false ######## FIRST CHECK ######## # Must be root to install echo ":::" if [[ ${EUID} -eq 0 ]]; then echo "::: You are root." else echo "::: Script called with non-root privileges. The Pi-hole installs server packages and configures" echo "::: system networking, it requires elevated rights. Please check the contents of the script for" echo "::: any concerns with this requirement. Please be sure to download this script from a trusted source." echo ":::" echo "::: Detecting the presence of the sudo utility for continuation of this install..." if [ -x "$(command -v sudo)" ]; then echo "::: Utility sudo located." exec curl -sSL https://install.pi-hole.net | sudo bash "$@" exit $? else echo "::: sudo is needed for the Web interface to run pihole commands. Please run this script as root and it will be automatically installed." exit 1 fi fi # Compatibility if [[ $(command -v apt-get) ]]; then #Debian Family ############################################# PKG_MANAGER="apt-get" PKG_CACHE="/var/lib/apt/lists/" UPDATE_PKG_CACHE="${PKG_MANAGER} update" PKG_UPDATE="${PKG_MANAGER} upgrade" PKG_INSTALL="${PKG_MANAGER} --yes --fix-missing install" # grep -c will return 1 retVal on 0 matches, block this throwing the set -e with an OR TRUE PKG_COUNT="${PKG_MANAGER} -s -o Debug::NoLocking=true upgrade | grep -c ^Inst || true" # ######################################### # fixes for dependancy differences # Debian 7 doesn't have iproute2 use iproute ${PKG_MANAGER} install --dry-run iproute2 > /dev/null 2>&1 && IPROUTE_PKG="iproute2" || IPROUTE_PKG="iproute" # Prefer the php metapackage if it's there, fall back on the php5 pacakges ${PKG_MANAGER} install --dry-run php > /dev/null 2>&1 && phpVer="php" || phpVer="php5" # ######################################### INSTALLER_DEPS=(apt-utils whiptail git dhcpcd5) PIHOLE_DEPS=(iputils-ping lsof dnsutils bc dnsmasq lighttpd ${phpVer}-common ${phpVer}-cgi curl unzip wget sudo netcat cron ${IPROUTE_PKG}) LIGHTTPD_USER="www-data" LIGHTTPD_GROUP="www-data" LIGHTTPD_CFG="lighttpd.conf.debian" DNSMASQ_USER="dnsmasq" package_check_install() { dpkg-query -W -f='${Status}' "${1}" 2>/dev/null | grep -c "ok installed" || ${PKG_INSTALL} "${1}" } elif [ $(command -v rpm) ]; then # Fedora Family if [ $(command -v dnf) ]; then PKG_MANAGER="dnf" else PKG_MANAGER="yum" fi PKG_CACHE="/var/cache/${PKG_MANAGER}" UPDATE_PKG_CACHE="${PKG_MANAGER} check-update" PKG_UPDATE="${PKG_MANAGER} update -y" PKG_INSTALL="${PKG_MANAGER} install -y" PKG_COUNT="${PKG_MANAGER} check-update | egrep '(.i686|.x86|.noarch|.arm|.src)' | wc -l" INSTALLER_DEPS=(iproute net-tools procps-ng newt git) PIHOLE_DEPS=(epel-release bind-utils bc dnsmasq lighttpd lighttpd-fastcgi php-common php-cli php curl unzip wget findutils cronie sudo nmap-ncat) if grep -q 'Fedora' /etc/redhat-release; then remove_deps=(epel-release); PIHOLE_DEPS=( ${PIHOLE_DEPS[@]/$remove_deps} ); fi LIGHTTPD_USER="lighttpd" LIGHTTPD_GROUP="lighttpd" LIGHTTPD_CFG="lighttpd.conf.fedora" DNSMASQ_USER="nobody" package_check_install() { rpm -qa | grep ^"${1}"- > /dev/null || ${PKG_INSTALL} "${1}" } else echo "OS distribution not supported" exit fi ####### FUNCTIONS ########## is_repo() { # Use git to check if directory is currently under VCS, return the value local directory="${1}" git -C "${directory}" status --short &> /dev/null return } make_repo() { local directory="${1}" local remoteRepo="${2}" # Remove the non-repod interface and clone the interface echo -n "::: Cloning $remoteRepo into $directory..." rm -rf "${directory}" git clone -q --depth 1 "${remoteRepo}" "${directory}" &> /dev/null echo " done!" } update_repo() { local directory="${1}" # Pull the latest commits echo -n "::: Updating repo in $1..." cd "${directory}" || exit 1 git stash -q &> /dev/null git pull -q &> /dev/null echo " done!" } getGitFiles() { # Setup git repos for directory and repository passed # as arguments 1 and 2 local directory="${1}" local remoteRepo="${2}" echo ":::" echo "::: Checking for existing repository..." if is_repo "${directory}"; then update_repo "${directory}" else make_repo "${directory}" "${remoteRepo}" fi } find_IPv4_information() { # Find IP used to route to outside world IPv4dev=$(ip route get 8.8.8.8 | awk '{for(i=1;i<=NF;i++)if($i~/dev/)print $(i+1)}') IPV4_ADDRESS=$(ip -o -f inet addr show dev "$IPv4dev" | awk '{print $4}' | awk 'END {print}') IPv4gw=$(ip route get 8.8.8.8 | awk '{print $3}') } get_available_interfaces() { # Get available interfaces. Consider only getting UP interfaces in the future, and leaving DOWN interfaces out of list. availableInterfaces=$(ip -o link | awk '{print $2}' | grep -v "lo" | cut -d':' -f1 | cut -d'@' -f1) } welcomeDialogs() { # Display the welcome dialog whiptail --msgbox --backtitle "Welcome" --title "Pi-hole automated installer" "\n\nThis installer will transform your device into a network-wide ad blocker!" ${r} ${c} # Support for a part-time dev whiptail --msgbox --backtitle "Plea" --title "Free and open source" "\n\nThe Pi-hole is free, but powered by your donations: http://pi-hole.net/donate" ${r} ${c} # Explain the need for a static address whiptail --msgbox --backtitle "Initiating network interface" --title "Static IP Needed" "\n\nThe Pi-hole is a SERVER so it needs a STATIC IP ADDRESS to function properly. In the next section, you can choose to use your current network settings (DHCP) or to manually edit them." ${r} ${c} } verifyFreeDiskSpace() { # 50MB is the minimum space needed (45MB install (includes web admin bootstrap/jquery libraries etc) + 5MB one day of logs.) # - Fourdee: Local ensures the variable is only created, and accessible within this function/void. Generally considered a "good" coding practice for non-global variables. echo "::: Verifying free disk space..." local required_free_kilobytes=51200 local existing_free_kilobytes=$(df -Pk | grep -m1 '\/$' | awk '{print $4}') # - Unknown free disk space , not a integer if ! [[ "${existing_free_kilobytes}" =~ ^([0-9])+$ ]]; then echo "::: Unknown free disk space!" echo "::: We were unable to determine available free disk space on this system." echo "::: You may override this check and force the installation, however, it is not recommended" echo "::: To do so, pass the argument '--i_do_not_follow_recommendations' to the install script" echo "::: eg. curl -L https://install.pi-hole.net | bash /dev/stdin --i_do_not_follow_recommendations" exit 1 # - Insufficient free disk space elif [[ ${existing_free_kilobytes} -lt ${required_free_kilobytes} ]]; then echo "::: Insufficient Disk Space!" echo "::: Your system appears to be low on disk space. pi-hole recommends a minimum of $required_free_kilobytes KiloBytes." echo "::: You only have ${existing_free_kilobytes} KiloBytes free." echo "::: If this is a new install you may need to expand your disk." echo "::: Try running 'sudo raspi-config', and choose the 'expand file system option'" echo "::: After rebooting, run this installation again. (curl -L https://install.pi-hole.net | bash)" echo "Insufficient free space, exiting..." exit 1 fi } chooseInterface() { # Turn the available interfaces into an array so it can be used with a whiptail dialog local interfacesArray=() # Number of available interfaces local interfaceCount # Whiptail variable storage local chooseInterfaceCmd # Temporary Whiptail options storage local chooseInterfaceOptions # Loop sentinel variable local firstLoop=1 while read -r line; do mode="OFF" if [[ ${firstLoop} -eq 1 ]]; then firstLoop=0 mode="ON" fi interfacesArray+=("${line}" "available" "${mode}") done <<< "${availableInterfaces}" # Find out how many interfaces are available to choose from interfaceCount=$(echo "${availableInterfaces}" | wc -l) chooseInterfaceCmd=(whiptail --separate-output --radiolist "Choose An Interface (press space to select)" ${r} ${c} ${interfaceCount}) chooseInterfaceOptions=$("${chooseInterfaceCmd[@]}" "${interfacesArray[@]}" 2>&1 >/dev/tty) if [[ $? = 0 ]]; then for desiredInterface in ${chooseInterfaceOptions}; do PIHOLE_INTERFACE=${desiredInterface} echo "::: Using interface: $PIHOLE_INTERFACE" done else echo "::: Cancel selected, exiting...." exit 1 fi } useIPv6dialog() { # Show the IPv6 address used for blocking IPV6_ADDRESS=$(ip -6 route get 2001:4860:4860::8888 | awk -F " " '{ for(i=1;i<=NF;i++) if ($i == "src") print $(i+1) }') whiptail --msgbox --backtitle "IPv6..." --title "IPv6 Supported" "$IPV6_ADDRESS will be used to block ads." ${r} ${c} } use4andor6() { local useIPv4 local useIPv6 # Let use select IPv4 and/or IPv6 cmd=(whiptail --separate-output --checklist "Select Protocols (press space to select)" ${r} ${c} 2) options=(IPv4 "Block ads over IPv4" on IPv6 "Block ads over IPv6" on) choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty) if [[ $? = 0 ]];then for choice in ${choices} do case ${choice} in IPv4 ) useIPv4=true;; IPv6 ) useIPv6=true;; esac done if [[ ${useIPv4} ]]; then find_IPv4_information getStaticIPv4Settings setStaticIPv4 fi if [[ ${useIPv6} ]]; then useIPv6dialog fi echo "::: IPv4 address: ${IPV4_ADDRESS}" echo "::: IPv6 address: ${IPV6_ADDRESS}" if [ ! ${useIPv4} ] && [ ! ${useIPv6} ]; then echo "::: Cannot continue, neither IPv4 or IPv6 selected" echo "::: Exiting" exit 1 fi else echo "::: Cancel selected. Exiting..." exit 1 fi } getStaticIPv4Settings() { # Ask if the user wants to use DHCP settings as their static IP if (whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Do you want to use your current network settings as a static address? IP address: ${IPV4_ADDRESS} Gateway: ${IPv4gw}" ${r} ${c}); then # If they choose yes, let the user know that the IP address will not be available via DHCP and may cause a conflict. whiptail --msgbox --backtitle "IP information" --title "FYI: IP Conflict" "It is possible your router could still try to assign this IP to a device, which would cause a conflict. But in most cases the router is smart enough to not do that. If you are worried, either manually set the address, or modify the DHCP reservation pool so it does not include the IP you want. It is also possible to use a DHCP reservation, but if you are going to do that, you might as well set a static address." ${r} ${c} # Nothing else to do since the variables are already set above else # Otherwise, we need to ask the user to input their desired settings. # Start by getting the IPv4 address (pre-filling it with info gathered from DHCP) # Start a loop to let the user enter their information with the chance to go back and edit it if necessary until [[ ${ipSettingsCorrect} = True ]]; do # Ask for the IPv4 address IPV4_ADDRESS=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 address" --inputbox "Enter your desired IPv4 address" ${r} ${c} "${IPV4_ADDRESS}" 3>&1 1>&2 2>&3) if [[ $? = 0 ]]; then echo "::: Your static IPv4 address: ${IPV4_ADDRESS}" # Ask for the gateway IPv4gw=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 gateway (router)" --inputbox "Enter your desired IPv4 default gateway" ${r} ${c} "${IPv4gw}" 3>&1 1>&2 2>&3) if [[ $? = 0 ]]; then echo "::: Your static IPv4 gateway: ${IPv4gw}" # Give the user a chance to review their settings before moving on if (whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Are these settings correct? IP address: ${IPV4_ADDRESS} Gateway: ${IPv4gw}" ${r} ${c}); then # After that's done, the loop ends and we move on ipSettingsCorrect=True else # If the settings are wrong, the loop continues ipSettingsCorrect=False fi else # Cancelling gateway settings window ipSettingsCorrect=False echo "::: Cancel selected. Exiting..." exit 1 fi else # Cancelling IPv4 settings window ipSettingsCorrect=False echo "::: Cancel selected. Exiting..." exit 1 fi done # End the if statement for DHCP vs. static fi } setDHCPCD() { # Append these lines to dhcpcd.conf to enable a static IP echo "## interface ${PIHOLE_INTERFACE} static ip_address=${IPV4_ADDRESS} static routers=${IPv4gw} static domain_name_servers=${IPv4gw}" | tee -a /etc/dhcpcd.conf >/dev/null } setStaticIPv4() { local IFCFG_FILE local IPADDR local CIDR if [[ -f /etc/dhcpcd.conf ]]; then # Debian Family if grep -q "${IPV4_ADDRESS}" /etc/dhcpcd.conf; then echo "::: Static IP already configured" else setDHCPCD ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" echo ":::" echo "::: Setting IP to ${IPV4_ADDRESS}. You may need to restart after the install is complete." echo ":::" fi elif [[ -f /etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE} ]];then # Fedora Family IFCFG_FILE=/etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE} if grep -q "${IPV4_ADDRESS}" "${IFCFG_FILE}"; then echo "::: Static IP already configured" else IPADDR=$(echo "${IPV4_ADDRESS}" | cut -f1 -d/) CIDR=$(echo "${IPV4_ADDRESS}" | cut -f2 -d/) # Backup existing interface configuration: cp "${IFCFG_FILE}" "${IFCFG_FILE}".pihole.orig # Build Interface configuration file: { echo "# Configured via Pi-Hole installer" echo "DEVICE=$PIHOLE_INTERFACE" echo "BOOTPROTO=none" echo "ONBOOT=yes" echo "IPADDR=$IPADDR" echo "PREFIX=$CIDR" echo "GATEWAY=$IPv4gw" echo "DNS1=$PIHOLE_DNS_1" echo "DNS2=$PIHOLE_DNS_2" echo "USERCTL=no" }> "${IFCFG_FILE}" ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" if [ -x "$(command -v nmcli)" ];then # Tell NetworkManager to read our new sysconfig file nmcli con load "${IFCFG_FILE}" > /dev/null fi echo ":::" echo "::: Setting IP to ${IPV4_ADDRESS}. You may need to restart after the install is complete." echo ":::" fi else echo "::: Warning: Unable to locate configuration file to set static IPv4 address!" exit 1 fi } valid_ip() { local ip=${1} local stat=1 if [[ ${ip} =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then OIFS=$IFS IFS='.' ip=(${ip}) IFS=${OIFS} [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \ && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]] stat=$? fi return ${stat} } setDNS() { DNSChooseCmd=(whiptail --separate-output --radiolist "Select Upstream DNS Provider. To use your own, select Custom." ${r} ${c} 6) DNSChooseOptions=(Google "" on OpenDNS "" off Level3 "" off Norton "" off Comodo "" off Custom "" off) DNSchoices=$("${DNSChooseCmd[@]}" "${DNSChooseOptions[@]}" 2>&1 >/dev/tty) if [[ $? = 0 ]];then case ${DNSchoices} in Google) echo "::: Using Google DNS servers." PIHOLE_DNS_1="8.8.8.8" PIHOLE_DNS_2="8.8.4.4" ;; OpenDNS) echo "::: Using OpenDNS servers." PIHOLE_DNS_1="208.67.222.222" PIHOLE_DNS_2="208.67.220.220" ;; Level3) echo "::: Using Level3 servers." PIHOLE_DNS_1="4.2.2.1" PIHOLE_DNS_2="4.2.2.2" ;; Norton) echo "::: Using Norton ConnectSafe servers." PIHOLE_DNS_1="199.85.126.10" PIHOLE_DNS_2="199.85.127.10" ;; Comodo) echo "::: Using Comodo Secure servers." PIHOLE_DNS_1="8.26.56.26" PIHOLE_DNS_2="8.20.247.20" ;; Custom) until [[ ${DNSSettingsCorrect} = True ]]; do strInvalid="Invalid" if [ ! ${PIHOLE_DNS_1} ]; then if [ ! ${PIHOLE_DNS_2} ]; then prePopulate="" else prePopulate=", ${PIHOLE_DNS_2}" fi elif [ ${PIHOLE_DNS_1} ] && [ ! ${PIHOLE_DNS_2} ]; then prePopulate="${PIHOLE_DNS_1}" elif [ ${PIHOLE_DNS_1} ] && [ ${PIHOLE_DNS_2} ]; then prePopulate="${PIHOLE_DNS_1}, ${PIHOLE_DNS_2}" fi piholeDNS=$(whiptail --backtitle "Specify Upstream DNS Provider(s)" --inputbox "Enter your desired upstream DNS provider(s), seperated by a comma.\n\nFor example '8.8.8.8, 8.8.4.4'" ${r} ${c} "${prePopulate}" 3>&1 1>&2 2>&3) if [[ $? = 0 ]]; then PIHOLE_DNS_1=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$1}') PIHOLE_DNS_2=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$2}') if ! valid_ip "${PIHOLE_DNS_1}" || [ ! "${PIHOLE_DNS_1}" ]; then PIHOLE_DNS_1=${strInvalid} fi if ! valid_ip "${PIHOLE_DNS_2}" && [ "${PIHOLE_DNS_2}" ]; then PIHOLE_DNS_2=${strInvalid} fi else echo "::: Cancel selected, exiting...." exit 1 fi if [[ ${PIHOLE_DNS_1} == "${strInvalid}" ]] || [[ ${PIHOLE_DNS_2} == "${strInvalid}" ]]; then whiptail --msgbox --backtitle "Invalid IP" --title "Invalid IP" "One or both entered IP addresses were invalid. Please try again.\n\n DNS Server 1: $PIHOLE_DNS_1\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c} if [[ ${PIHOLE_DNS_1} == "${strInvalid}" ]]; then PIHOLE_DNS_1="" fi if [[ ${PIHOLE_DNS_2} == "${strInvalid}" ]]; then PIHOLE_DNS_2="" fi DNSSettingsCorrect=False else if (whiptail --backtitle "Specify Upstream DNS Provider(s)" --title "Upstream DNS Provider(s)" --yesno "Are these settings correct?\n DNS Server 1: $PIHOLE_DNS_1\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c}); then DNSSettingsCorrect=True else # If the settings are wrong, the loop continues DNSSettingsCorrect=False fi fi done ;; esac else echo "::: Cancel selected. Exiting..." exit 1 fi } setLogging() { local LogToggleCommand local LogChooseOptions local LogChoices LogToggleCommand=(whiptail --separate-output --radiolist "Do you want to log queries?\n (Disabling will render graphs on the Admin page useless):" ${r} ${c} 6) LogChooseOptions=("On (Reccomended)" "" on Off "" off) LogChoices=$("${LogToggleCommand[@]}" "${LogChooseOptions[@]}" 2>&1 >/dev/tty) || (echo "::: Cancel selected. Exiting..." && exit 1) case ${LogChoices} in "On (Recommended)") echo "::: Logging On." QUERY_LOGGING=true ;; Off) echo "::: Logging Off." QUERY_LOGGING=false ;; esac } version_check_dnsmasq() { # Check if /etc/dnsmasq.conf is from pihole. If so replace with an original and install new in .d directory local dnsmasq_conf="/etc/dnsmasq.conf" local dnsmasq_conf_orig="/etc/dnsmasq.conf.orig" local dnsmasq_pihole_id_string="addn-hosts=/etc/pihole/gravity.list" local dnsmasq_original_config="/etc/.pihole/advanced/dnsmasq.conf.original" local dnsmasq_pihole_01_snippet="/etc/.pihole/advanced/01-pihole.conf" local dnsmasq_pihole_01_location="/etc/dnsmasq.d/01-pihole.conf" if [ -f ${dnsmasq_conf} ]; then echo -n "::: Existing dnsmasq.conf found..." if grep -q ${dnsmasq_pihole_id_string} ${dnsmasq_conf}; then echo " it is from a previous pi-hole install." echo -n "::: Backing up dnsmasq.conf to dnsmasq.conf.orig..." mv -f ${dnsmasq_conf} ${dnsmasq_conf_orig} echo " done." echo -n "::: Restoring default dnsmasq.conf..." cp ${dnsmasq_original_config} ${dnsmasq_conf} echo " done." else echo " it is not a pi-hole file, leaving alone!" fi else echo -n "::: No dnsmasq.conf found.. restoring default dnsmasq.conf..." cp ${dnsmasq_original_config} ${dnsmasq_conf} echo " done." fi echo -n "::: Copying 01-pihole.conf to /etc/dnsmasq.d/01-pihole.conf..." cp ${dnsmasq_pihole_01_snippet} ${dnsmasq_pihole_01_location} echo " done." sed -i "s/@INT@/$PIHOLE_INTERFACE/" ${dnsmasq_pihole_01_location} if [[ "${PIHOLE_DNS_1}" != "" ]]; then sed -i "s/@DNS1@/$PIHOLE_DNS_1/" ${dnsmasq_pihole_01_location} else sed -i '/^server=@DNS1@/d' ${dnsmasq_pihole_01_location} fi if [[ "${PIHOLE_DNS_2}" != "" ]]; then sed -i "s/@DNS2@/$PIHOLE_DNS_2/" ${dnsmasq_pihole_01_location} else sed -i '/^server=@DNS2@/d' ${dnsmasq_pihole_01_location} fi sed -i 's/^#conf-dir=\/etc\/dnsmasq.d$/conf-dir=\/etc\/dnsmasq.d/' ${dnsmasq_conf} if [[ "${QUERY_LOGGING}" == false ]] ; then #Disable Logging sed -i 's/^log-queries/#log-queries/' ${dnsmasq_pihole_01_location} else #Enable Logging sed -i 's/^#log-queries/log-queries/' ${dnsmasq_pihole_01_location} fi } remove_legacy_scripts() { #Tidy up /usr/local/bin directory if installing over previous install. oldFiles=( gravity chronometer whitelist blacklist piholeLogFlush updateDashboard uninstall setupLCD piholeDebug) for i in "${oldFiles[@]}"; do if [ -f "/usr/local/bin/$i.sh" ]; then rm /usr/local/bin/"$i".sh fi done } clean_existing() { # Clean an exiting installation to prepare for upgrade/reinstall # ${1} Directory to clean; ${2} Array of files to remove local clean_directory="${1}" local old_files=${2} for script in "${old_files[@]}"; do rm -f "${clean_directory}${script}.sh" done } installScripts() { # Install the scripts from repository to their various locations readonly install_dir="/opt/pihole/" echo ":::" echo -n "::: Installing scripts from ${PI_HOLE_LOCAL_REPO}..." # Clear out script files from Pi-hole scripts directory. clean_existing "${install_dir}" "${PI_HOLE_FILES}" # Install files from local core repository if is_repo "${PI_HOLE_LOCAL_REPO}"; then cd "${PI_HOLE_LOCAL_REPO}" install -o "${USER}" -Dm755 -t /opt/pihole/ gravity.sh install -o "${USER}" -Dm755 -t /opt/pihole/ ./advanced/Scripts/*.sh install -o "${USER}" -Dm755 -t /opt/pihole/ ./automated\ install/uninstall.sh install -o "${USER}" -Dm755 -t /usr/local/bin/ pihole install -Dm644 ./advanced/bash-completion/pihole /etc/bash_completion.d/pihole echo " done." else echo " *** ERROR: Local repo ${core_repo} not found, exiting." exit 1 fi } installConfigs() { # Install the configs from /etc/.pihole to their various locations echo ":::" echo "::: Installing configs..." version_check_dnsmasq if [ ! -d "/etc/lighttpd" ]; then mkdir /etc/lighttpd chown "${USER}":root /etc/lighttpd elif [ -f "/etc/lighttpd/lighttpd.conf" ]; then mv /etc/lighttpd/lighttpd.conf /etc/lighttpd/lighttpd.conf.orig fi cp /etc/.pihole/advanced/${LIGHTTPD_CFG} /etc/lighttpd/lighttpd.conf mkdir -p /var/run/lighttpd chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/run/lighttpd mkdir -p /var/cache/lighttpd/compress chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/compress mkdir -p /var/cache/lighttpd/uploads chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/uploads } stop_service() { # Stop service passed in as argument. # Can softfail, as process may not be installed when this is called echo ":::" echo -n "::: Stopping ${1} service..." if [ -x "$(command -v systemctl)" ]; then systemctl stop "${1}" &> /dev/null || true else service "${1}" stop &> /dev/null || true fi echo " done." } start_service() { # Start/Restart service passed in as argument # This should not fail, it's an error if it does echo ":::" echo -n "::: Starting ${1} service..." if [ -x "$(command -v systemctl)" ]; then systemctl restart "${1}" &> /dev/null else service "${1}" restart &> /dev/null fi echo " done." } enable_service() { # Enable service so that it will start with next reboot echo ":::" echo -n "::: Enabling ${1} service to start on reboot..." if [ -x "$(command -v systemctl)" ]; then systemctl enable "${1}" &> /dev/null else update-rc.d "${1}" defaults &> /dev/null fi echo " done." } update_pacakge_cache() { #Running apt-get update/upgrade with minimal output can cause some issues with #requiring user input (e.g password for phpmyadmin see #218) #Check to see if apt-get update has already been run today #it needs to have been run at least once on new installs! timestamp=$(stat -c %Y ${PKG_CACHE}) timestampAsDate=$(date -d @"${timestamp}" "+%b %e") today=$(date "+%b %e") if [ ! "${today}" == "${timestampAsDate}" ]; then #update package lists echo ":::" echo -n "::: ${PKG_MANAGER} update has not been run today. Running now..." ${UPDATE_PKG_CACHE} &> /dev/null echo " done!" fi } notify_package_updates_available() { # Let user know if they have outdated packages on their system and # advise them to run a package update at soonest possible. echo ":::" echo -n "::: Checking ${PKG_MANAGER} for upgraded packages...." updatesToInstall=$(eval "${PKG_COUNT}") echo " done!" echo ":::" if [[ ${updatesToInstall} -eq "0" ]]; then echo "::: Your system is up to date! Continuing with Pi-hole installation..." else echo "::: There are ${updatesToInstall} updates available for your system!" echo "::: We recommend you run '${PKG_UPDATE}' after installing Pi-Hole! " echo ":::" fi } install_dependent_packages() { # Install packages passed in via argument array # No spinner - conflicts with set -e declare -a argArray1=("${!1}") for i in "${argArray1[@]}"; do echo -n "::: Checking for $i..." package_check_install "${i}" &> /dev/null echo " installed!" done } CreateLogFile() { # Create logfiles if necessary echo ":::" echo -n "::: Creating log file and changing owner to dnsmasq..." if [ ! -f /var/log/pihole.log ]; then touch /var/log/pihole.log chmod 644 /var/log/pihole.log chown "${DNSMASQ_USER}":root /var/log/pihole.log echo " done!" else echo " already exists!" fi } installPiholeWeb() { # Install the web interface echo ":::" echo "::: Installing pihole custom index page..." if [ -d "/var/www/html/pihole" ]; then if [ -f "/var/www/html/pihole/index.html" ]; then echo "::: Existing index.html detected, not overwriting" else echo -n "::: index.html missing, replacing... " cp /etc/.pihole/advanced/index.html /var/www/html/pihole/ echo " done!" fi if [ -f "/var/www/html/pihole/index.js" ]; then echo "::: Existing index.js detected, not overwriting" else echo -n "::: index.js missing, replacing... " cp /etc/.pihole/advanced/index.js /var/www/html/pihole/ echo " done!" fi else mkdir /var/www/html/pihole if [ -f /var/www/html/index.lighttpd.html ]; then mv /var/www/html/index.lighttpd.html /var/www/html/index.lighttpd.orig else printf "\n:::\tNo default index.lighttpd.html file found... not backing up" fi cp /etc/.pihole/advanced/index.* /var/www/html/pihole/. echo " done!" fi # Install Sudoer file echo ":::" echo -n "::: Installing sudoer file..." mkdir -p /etc/sudoers.d/ cp /etc/.pihole/advanced/pihole.sudo /etc/sudoers.d/pihole chmod 0440 /etc/sudoers.d/pihole echo " done!" } installCron() { # Install the cron job echo ":::" echo -n "::: Installing latest Cron script..." cp /etc/.pihole/advanced/pihole.cron /etc/cron.d/pihole echo " done!" } runGravity() { # Run gravity.sh to build blacklists echo ":::" echo "::: Preparing to run gravity.sh to refresh hosts..." if ls /etc/pihole/list* 1> /dev/null 2>&1; then echo "::: Cleaning up previous install (preserving whitelist/blacklist)" rm /etc/pihole/list.* fi echo "::: Running gravity.sh" /opt/pihole/gravity.sh } create_pihole_user() { # Check if user pihole exists and create if not echo "::: Checking if user 'pihole' exists..." id -u pihole &> /dev/null && echo "::: User 'pihole' already exists" || (echo "::: User 'pihole' doesn't exist. Creating..." && useradd -r -s /usr/sbin/nologin pihole) } configureFirewall() { # Allow HTTP and DNS traffic if [ -x "$(command -v firewall-cmd)" ]; then firewall-cmd --state &> /dev/null && ( echo "::: Configuring firewalld for httpd and dnsmasq.." && firewall-cmd --permanent --add-port=80/tcp && firewall-cmd --permanent --add-port=53/tcp \ && firewall-cmd --permanent --add-port=53/udp && firewall-cmd --reload) || echo "::: FirewallD not enabled" elif [ -x "$(command -v iptables)" ]; then echo "::: Configuring iptables for httpd and dnsmasq.." iptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT iptables -A INPUT -p tcp -m tcp --dport 53 -j ACCEPT iptables -A INPUT -p udp -m udp --dport 53 -j ACCEPT else echo "::: No firewall detected.. skipping firewall configuration." fi } finalExports() { # Update variables in setupVars.conf file if [ -e "${setupVars}" ]; then sed -i.update.bak '/PIHOLE_INTERFACE/d;/IPV4_ADDRESS/d;/IPV6_ADDRESS/d;/PIHOLE_DNS_1/d;/PIHOLE_DNS_2/d;/QUERY_LOGGING/d;' "${setupVars}" fi { echo "PIHOLE_INTERFACE=${PIHOLE_INTERFACE}" echo "IPV4_ADDRESS=${IPV4_ADDRESS}" echo "IPV6_ADDRESS=${IPV6_ADDRESS}" echo "PIHOLE_DNS_1=${PIHOLE_DNS_1}" echo "PIHOLE_DNS_2=${PIHOLE_DNS_2}" echo "QUERY_LOGGING=${QUERY_LOGGING}" }>> "${setupVars}" } installPihole() { # Install base files and web interface create_pihole_user if [ ! -d "/var/www/html" ]; then mkdir -p /var/www/html fi chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/www/html chmod 775 /var/www/html usermod -a -G ${LIGHTTPD_GROUP} pihole if [ -x "$(command -v lighty-enable-mod)" ]; then lighty-enable-mod fastcgi fastcgi-php > /dev/null || true else printf "\n:::\tWarning: 'lighty-enable-mod' utility not found. Please ensure fastcgi is enabled if you experience issues.\n" fi installScripts installConfigs CreateLogFile configureSelinux installPiholeWeb installCron configureFirewall finalExports runGravity } accountForRefactor() { # At some point in the future this list can be pruned, for now we'll need it to ensure updates don't break. # Refactoring of install script has changed the name of a couple of variables. Sort them out here. sed -i 's/piholeInterface/PIHOLE_INTERFACE/g' ${setupVars} sed -i 's/IPv4_address/IPV4_ADDRESS/g' ${setupVars} sed -i 's/IPv4addr/IPV4_ADDRESS/g' ${setupVars} sed -i 's/IPv6_address/IPV6_ADDRESS/g' ${setupVars} sed -i 's/piholeIPv6/IPV6_ADDRESS/g' ${setupVars} sed -i 's/piholeDNS1/PIHOLE_DNS_1/g' ${setupVars} sed -i 's/piholeDNS2/PIHOLE_DNS_2/g' ${setupVars} } updatePihole() { accountForRefactor # Source ${setupVars} for use in the rest of the functions. . ${setupVars} # Install base files and web interface installScripts installConfigs CreateLogFile configureSelinux installPiholeWeb installCron configureFirewall finalExports #re-export setupVars.conf to account for any new vars added in new versions runGravity } configureSelinux() { if [ -x "$(command -v getenforce)" ]; then printf "\n::: SELinux Detected\n" printf ":::\tChecking for SELinux policy development packages..." package_check_install "selinux-policy-devel" > /dev/null echo " installed!" printf ":::\tEnabling httpd server side includes (SSI).. " setsebool -P httpd_ssi_exec on &> /dev/null && echo "Success" || echo "SELinux not enabled" printf "\n:::\tCompiling Pi-Hole SELinux policy..\n" if ! [ -x "$(command -v systemctl)" ]; then sed -i.bak '/systemd/d' /etc/.pihole/advanced/selinux/pihole.te fi checkmodule -M -m -o /etc/pihole/pihole.mod /etc/.pihole/advanced/selinux/pihole.te semodule_package -o /etc/pihole/pihole.pp -m /etc/pihole/pihole.mod semodule -i /etc/pihole/pihole.pp rm -f /etc/pihole/pihole.mod semodule -l | grep pihole &> /dev/null && echo "::: Installed Pi-Hole SELinux policy" || echo "::: Warning: Pi-Hole SELinux policy did not install." fi } displayFinalMessage() { if (( ${#1} > 0 )) ; then # Final completion message to user whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: IPv4: ${IPV4_ADDRESS%/*} IPv6: ${IPV6_ADDRESS} If you set a new IP address, you should restart the Pi. The install log is in /etc/pihole. View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin The currently set password is ${1}" ${r} ${c} else whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: IPv4: ${IPV4_ADDRESS%/*} IPv6: ${IPV6_ADDRESS} If you set a new IP address, you should restart the Pi. The install log is in /etc/pihole. View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin" ${r} ${c} fi } update_dialogs() { # reconfigure if [ "${reconfigure}" = true ]; then opt1a="Repair" opt1b="This will retain existing settings" strAdd="You will remain on the same version" else opt1a="Update" opt1b="This will retain existing settings." strAdd="You will be updated to the latest version." fi opt2a="Reconfigure" opt2b="This will allow you to enter new settings" UpdateCmd=$(whiptail --title "Existing Install Detected!" --menu "\n\nWe have detected an existing install.\n\nPlease choose from the following options: \n($strAdd)" ${r} ${c} 2 \ "${opt1a}" "${opt1b}" \ "${opt2a}" "${opt2b}" 3>&2 2>&1 1>&3) if [[ $? = 0 ]];then case ${UpdateCmd} in ${opt1a}) echo "::: ${opt1a} option selected." useUpdateVars=true ;; ${opt2a}) echo "::: ${opt2a} option selected" useUpdateVars=false ;; esac else echo "::: Cancel selected. Exiting..." exit 1 fi } main() { # Check arguments for the undocumented flags for var in "$@"; do case "$var" in "--reconfigure" ) reconfigure=true;; "--i_do_not_follow_recommendations" ) skipSpaceCheck=false;; "--unattended" ) runUnattended=true;; esac done if [[ -f ${setupVars} ]]; then if [[ "${runUnattended}" == true ]]; then echo "::: --unattended passed to install script, no whiptail dialogs will be displayed" useUpdateVars=true else update_dialogs fi fi # Start the installer # Verify there is enough disk space for the install if [[ "${skipSpaceCheck}" == true ]]; then echo "::: --i_do_not_follow_recommendations passed to script, skipping free disk space verification!" else verifyFreeDiskSpace fi # Update package cache update_pacakge_cache # Notify user of package availability notify_package_updates_available # Install packages used by this installation script install_dependent_packages INSTALLER_DEPS[@] if [[ "${reconfigure}" == true ]]; then echo "::: --reconfigure passed to install script. Not downloading/updating local repos" else # Get Git files for Core and Admin getGitFiles ${PI_HOLE_LOCAL_REPO} ${piholeGitUrl} getGitFiles ${webInterfaceDir} ${webInterfaceGitUrl} fi if [[ ${useUpdateVars} == false ]]; then # Display welcome dialogs welcomeDialogs # Create directory for Pi-hole storage mkdir -p /etc/pihole/ # Remove legacy scripts from previous storage location remove_legacy_scripts # Stop resolver and webserver while installing proceses stop_service dnsmasq stop_service lighttpd # Determine available interfaces get_available_interfaces # Find interfaces and let the user choose one chooseInterface # Let the user decide if they want to block ads over IPv4 and/or IPv6 use4andor6 # Decide what upstream DNS Servers to use setDNS # Let the user decide if they want query logging enabled... setLogging # Install packages used by the Pi-hole install_dependent_packages PIHOLE_DEPS[@] # Install and log everything to a file installPihole | tee ${tmpLog} else # update packages used by the Pi-hole install_dependent_packages PIHOLE_DEPS[@] updatePihole | tee ${tmpLog} fi # Move the log file into /etc/pihole for storage mv ${tmpLog} ${instalLogLoc} # Add password to web UI if there is none pw="" if [[ $(grep 'WEBPASSWORD' -c /etc/pihole/setupVars.conf) == 0 ]] ; then pw=$(tr -dc _A-Z-a-z-0-9 < /dev/urandom | head -c 8) pihole -a -p ${pw} fi if [[ "${useUpdateVars}" == false ]]; then displayFinalMessage ${pw} fi echo "::: Restarting services..." # Start services start_service dnsmasq enable_service dnsmasq start_service lighttpd enable_service lighttpd echo "::: done." echo ":::" if [[ "${useUpdateVars}" == false ]]; then echo "::: Installation Complete! Configure your devices to use the Pi-hole as their DNS server using:" echo "::: ${IPV4_ADDRESS%/*}" echo "::: ${IPV6_ADDRESS}" echo ":::" echo "::: If you set a new IP address, you should restart the Pi." echo "::: View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin" else echo "::: Update complete!" fi if (( ${#pw} > 0 )) ; then echo ":::" echo "::: Note: As security measure a password has been installed for your web interface" echo "::: The currently set password is" echo "::: ${pw}" echo ":::" echo "::: You can always change it using" echo "::: pihole -a -p new_password" fi echo ":::" echo "::: The install log is located at: /etc/pihole/install.log" } if [[ "${PH_TEST}" != true ]] ; then main "$@" fi \ No newline at end of file +#!/usr/bin/env bash # Pi-hole: A black hole for Internet advertisements # (c) 2015, 2016 by Jacob Salmela # Network-wide ad blocking via your Raspberry Pi # http://pi-hole.net # Installs Pi-hole # # Pi-hole is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # pi-hole.net/donate # # Install with this command (from your Pi): # # curl -L install.pi-hole.net | bash set -e ######## VARIABLES ######### tmpLog=/tmp/pihole-install.log instalLogLoc=/etc/pihole/install.log setupVars=/etc/pihole/setupVars.conf webInterfaceGitUrl="https://github.com/pi-hole/AdminLTE.git" webInterfaceDir="/var/www/html/admin" piholeGitUrl="https://github.com/pi-hole/pi-hole.git" PI_HOLE_LOCAL_REPO="/etc/.pihole" PI_HOLE_FILES=(chronometer list piholeDebug piholeLogFlush setupLCD update version) useUpdateVars=false IPV4_ADDRESS="" IPV6_ADDRESS="" QUERY_LOGGING=true # Find the rows and columns will default to 80x24 is it can not be detected screen_size=$(stty size 2>/dev/null || echo 24 80) rows=$(echo $screen_size | awk '{print $1}') columns=$(echo $screen_size | awk '{print $2}') # Divide by two so the dialogs take up half of the screen, which looks nice. r=$(( rows / 2 )) c=$(( columns / 2 )) # Unless the screen is tiny r=$(( r < 20 ? 20 : r )) c=$(( c < 70 ? 70 : c )) ######## Undocumented Flags. Shhh ######## skipSpaceCheck=false reconfigure=false runUnattended=false ######## FIRST CHECK ######## # Must be root to install echo ":::" if [[ ${EUID} -eq 0 ]]; then echo "::: You are root." else echo "::: Script called with non-root privileges. The Pi-hole installs server packages and configures" echo "::: system networking, it requires elevated rights. Please check the contents of the script for" echo "::: any concerns with this requirement. Please be sure to download this script from a trusted source." echo ":::" echo "::: Detecting the presence of the sudo utility for continuation of this install..." if [ -x "$(command -v sudo)" ]; then echo "::: Utility sudo located." exec curl -sSL https://install.pi-hole.net | sudo bash "$@" exit $? else echo "::: sudo is needed for the Web interface to run pihole commands. Please run this script as root and it will be automatically installed." exit 1 fi fi # Compatibility if [[ $(command -v apt-get) ]]; then #Debian Family ############################################# PKG_MANAGER="apt-get" PKG_CACHE="/var/lib/apt/lists/" UPDATE_PKG_CACHE="${PKG_MANAGER} update" PKG_UPDATE="${PKG_MANAGER} upgrade" PKG_INSTALL="${PKG_MANAGER} --yes --fix-missing install" # grep -c will return 1 retVal on 0 matches, block this throwing the set -e with an OR TRUE PKG_COUNT="${PKG_MANAGER} -s -o Debug::NoLocking=true upgrade | grep -c ^Inst || true" # ######################################### # fixes for dependancy differences # Debian 7 doesn't have iproute2 use iproute ${PKG_MANAGER} install --dry-run iproute2 > /dev/null 2>&1 && IPROUTE_PKG="iproute2" || IPROUTE_PKG="iproute" # Prefer the php metapackage if it's there, fall back on the php5 pacakges ${PKG_MANAGER} install --dry-run php > /dev/null 2>&1 && phpVer="php" || phpVer="php5" # ######################################### INSTALLER_DEPS=(apt-utils debconf dhcpcd5 git whiptail) PIHOLE_DEPS=(bc cron curl dnsmasq dnsutils ${IPROUTE_PKG} iputils-ping lighttpd lsof netcat ${phpVer}-common ${phpVer}-cgi sudo unzip wget) LIGHTTPD_USER="www-data" LIGHTTPD_GROUP="www-data" LIGHTTPD_CFG="lighttpd.conf.debian" DNSMASQ_USER="dnsmasq" package_check_install() { dpkg-query -W -f='${Status}' "${1}" 2>/dev/null | grep -c "ok installed" || ${PKG_INSTALL} "${1}" } elif [ $(command -v rpm) ]; then # Fedora Family if [ $(command -v dnf) ]; then PKG_MANAGER="dnf" else PKG_MANAGER="yum" fi PKG_CACHE="/var/cache/${PKG_MANAGER}" UPDATE_PKG_CACHE="${PKG_MANAGER} check-update" PKG_UPDATE="${PKG_MANAGER} update -y" PKG_INSTALL="${PKG_MANAGER} install -y" PKG_COUNT="${PKG_MANAGER} check-update | egrep '(.i686|.x86|.noarch|.arm|.src)' | wc -l" INSTALLER_DEPS=(git iproute net-tools newt procps-ng) PIHOLE_DEPS=(bc bind-utils cronie curl dnsmasq epel-release findutils lighttpd lighttpd-fastcgi nmap-ncat php php-common php-cli sudo unzip wget) if grep -q 'Fedora' /etc/redhat-release; then remove_deps=(epel-release); PIHOLE_DEPS=( ${PIHOLE_DEPS[@]/$remove_deps} ); fi LIGHTTPD_USER="lighttpd" LIGHTTPD_GROUP="lighttpd" LIGHTTPD_CFG="lighttpd.conf.fedora" DNSMASQ_USER="nobody" package_check_install() { rpm -qa | grep ^"${1}"- > /dev/null || ${PKG_INSTALL} "${1}" } else echo "OS distribution not supported" exit fi ####### FUNCTIONS ########## is_repo() { # Use git to check if directory is currently under VCS, return the value local directory="${1}" git -C "${directory}" status --short &> /dev/null return } make_repo() { local directory="${1}" local remoteRepo="${2}" # Remove the non-repod interface and clone the interface echo -n "::: Cloning $remoteRepo into $directory..." rm -rf "${directory}" git clone -q --depth 1 "${remoteRepo}" "${directory}" &> /dev/null echo " done!" } update_repo() { local directory="${1}" # Pull the latest commits echo -n "::: Updating repo in $1..." cd "${directory}" || exit 1 git stash -q &> /dev/null git pull -q &> /dev/null echo " done!" } getGitFiles() { # Setup git repos for directory and repository passed # as arguments 1 and 2 local directory="${1}" local remoteRepo="${2}" echo ":::" echo "::: Checking for existing repository..." if is_repo "${directory}"; then update_repo "${directory}" else make_repo "${directory}" "${remoteRepo}" fi } find_IPv4_information() { # Find IP used to route to outside world IPv4dev=$(ip route get 8.8.8.8 | awk '{for(i=1;i<=NF;i++)if($i~/dev/)print $(i+1)}') IPV4_ADDRESS=$(ip -o -f inet addr show dev "$IPv4dev" | awk '{print $4}' | awk 'END {print}') IPv4gw=$(ip route get 8.8.8.8 | awk '{print $3}') } get_available_interfaces() { # Get available interfaces. Consider only getting UP interfaces in the future, and leaving DOWN interfaces out of list. availableInterfaces=$(ip -o link | awk '{print $2}' | grep -v "lo" | cut -d':' -f1 | cut -d'@' -f1) } welcomeDialogs() { # Display the welcome dialog whiptail --msgbox --backtitle "Welcome" --title "Pi-hole automated installer" "\n\nThis installer will transform your device into a network-wide ad blocker!" ${r} ${c} # Support for a part-time dev whiptail --msgbox --backtitle "Plea" --title "Free and open source" "\n\nThe Pi-hole is free, but powered by your donations: http://pi-hole.net/donate" ${r} ${c} # Explain the need for a static address whiptail --msgbox --backtitle "Initiating network interface" --title "Static IP Needed" "\n\nThe Pi-hole is a SERVER so it needs a STATIC IP ADDRESS to function properly. In the next section, you can choose to use your current network settings (DHCP) or to manually edit them." ${r} ${c} } verifyFreeDiskSpace() { # 50MB is the minimum space needed (45MB install (includes web admin bootstrap/jquery libraries etc) + 5MB one day of logs.) # - Fourdee: Local ensures the variable is only created, and accessible within this function/void. Generally considered a "good" coding practice for non-global variables. echo "::: Verifying free disk space..." local required_free_kilobytes=51200 local existing_free_kilobytes=$(df -Pk | grep -m1 '\/$' | awk '{print $4}') # - Unknown free disk space , not a integer if ! [[ "${existing_free_kilobytes}" =~ ^([0-9])+$ ]]; then echo "::: Unknown free disk space!" echo "::: We were unable to determine available free disk space on this system." echo "::: You may override this check and force the installation, however, it is not recommended" echo "::: To do so, pass the argument '--i_do_not_follow_recommendations' to the install script" echo "::: eg. curl -L https://install.pi-hole.net | bash /dev/stdin --i_do_not_follow_recommendations" exit 1 # - Insufficient free disk space elif [[ ${existing_free_kilobytes} -lt ${required_free_kilobytes} ]]; then echo "::: Insufficient Disk Space!" echo "::: Your system appears to be low on disk space. pi-hole recommends a minimum of $required_free_kilobytes KiloBytes." echo "::: You only have ${existing_free_kilobytes} KiloBytes free." echo "::: If this is a new install you may need to expand your disk." echo "::: Try running 'sudo raspi-config', and choose the 'expand file system option'" echo "::: After rebooting, run this installation again. (curl -L https://install.pi-hole.net | bash)" echo "Insufficient free space, exiting..." exit 1 fi } chooseInterface() { # Turn the available interfaces into an array so it can be used with a whiptail dialog local interfacesArray=() # Number of available interfaces local interfaceCount # Whiptail variable storage local chooseInterfaceCmd # Temporary Whiptail options storage local chooseInterfaceOptions # Loop sentinel variable local firstLoop=1 while read -r line; do mode="OFF" if [[ ${firstLoop} -eq 1 ]]; then firstLoop=0 mode="ON" fi interfacesArray+=("${line}" "available" "${mode}") done <<< "${availableInterfaces}" # Find out how many interfaces are available to choose from interfaceCount=$(echo "${availableInterfaces}" | wc -l) chooseInterfaceCmd=(whiptail --separate-output --radiolist "Choose An Interface (press space to select)" ${r} ${c} ${interfaceCount}) chooseInterfaceOptions=$("${chooseInterfaceCmd[@]}" "${interfacesArray[@]}" 2>&1 >/dev/tty) if [[ $? = 0 ]]; then for desiredInterface in ${chooseInterfaceOptions}; do PIHOLE_INTERFACE=${desiredInterface} echo "::: Using interface: $PIHOLE_INTERFACE" done else echo "::: Cancel selected, exiting...." exit 1 fi } useIPv6dialog() { # Show the IPv6 address used for blocking IPV6_ADDRESS=$(ip -6 route get 2001:4860:4860::8888 | awk -F " " '{ for(i=1;i<=NF;i++) if ($i == "src") print $(i+1) }') whiptail --msgbox --backtitle "IPv6..." --title "IPv6 Supported" "$IPV6_ADDRESS will be used to block ads." ${r} ${c} } use4andor6() { local useIPv4 local useIPv6 # Let use select IPv4 and/or IPv6 cmd=(whiptail --separate-output --checklist "Select Protocols (press space to select)" ${r} ${c} 2) options=(IPv4 "Block ads over IPv4" on IPv6 "Block ads over IPv6" on) choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty) if [[ $? = 0 ]];then for choice in ${choices} do case ${choice} in IPv4 ) useIPv4=true;; IPv6 ) useIPv6=true;; esac done if [[ ${useIPv4} ]]; then find_IPv4_information getStaticIPv4Settings setStaticIPv4 fi if [[ ${useIPv6} ]]; then useIPv6dialog fi echo "::: IPv4 address: ${IPV4_ADDRESS}" echo "::: IPv6 address: ${IPV6_ADDRESS}" if [ ! ${useIPv4} ] && [ ! ${useIPv6} ]; then echo "::: Cannot continue, neither IPv4 or IPv6 selected" echo "::: Exiting" exit 1 fi else echo "::: Cancel selected. Exiting..." exit 1 fi } getStaticIPv4Settings() { # Ask if the user wants to use DHCP settings as their static IP if (whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Do you want to use your current network settings as a static address? IP address: ${IPV4_ADDRESS} Gateway: ${IPv4gw}" ${r} ${c}); then # If they choose yes, let the user know that the IP address will not be available via DHCP and may cause a conflict. whiptail --msgbox --backtitle "IP information" --title "FYI: IP Conflict" "It is possible your router could still try to assign this IP to a device, which would cause a conflict. But in most cases the router is smart enough to not do that. If you are worried, either manually set the address, or modify the DHCP reservation pool so it does not include the IP you want. It is also possible to use a DHCP reservation, but if you are going to do that, you might as well set a static address." ${r} ${c} # Nothing else to do since the variables are already set above else # Otherwise, we need to ask the user to input their desired settings. # Start by getting the IPv4 address (pre-filling it with info gathered from DHCP) # Start a loop to let the user enter their information with the chance to go back and edit it if necessary until [[ ${ipSettingsCorrect} = True ]]; do # Ask for the IPv4 address IPV4_ADDRESS=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 address" --inputbox "Enter your desired IPv4 address" ${r} ${c} "${IPV4_ADDRESS}" 3>&1 1>&2 2>&3) if [[ $? = 0 ]]; then echo "::: Your static IPv4 address: ${IPV4_ADDRESS}" # Ask for the gateway IPv4gw=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 gateway (router)" --inputbox "Enter your desired IPv4 default gateway" ${r} ${c} "${IPv4gw}" 3>&1 1>&2 2>&3) if [[ $? = 0 ]]; then echo "::: Your static IPv4 gateway: ${IPv4gw}" # Give the user a chance to review their settings before moving on if (whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Are these settings correct? IP address: ${IPV4_ADDRESS} Gateway: ${IPv4gw}" ${r} ${c}); then # After that's done, the loop ends and we move on ipSettingsCorrect=True else # If the settings are wrong, the loop continues ipSettingsCorrect=False fi else # Cancelling gateway settings window ipSettingsCorrect=False echo "::: Cancel selected. Exiting..." exit 1 fi else # Cancelling IPv4 settings window ipSettingsCorrect=False echo "::: Cancel selected. Exiting..." exit 1 fi done # End the if statement for DHCP vs. static fi } setDHCPCD() { # Append these lines to dhcpcd.conf to enable a static IP echo "## interface ${PIHOLE_INTERFACE} static ip_address=${IPV4_ADDRESS} static routers=${IPv4gw} static domain_name_servers=${IPv4gw}" | tee -a /etc/dhcpcd.conf >/dev/null } setStaticIPv4() { local IFCFG_FILE local IPADDR local CIDR if [[ -f /etc/dhcpcd.conf ]]; then # Debian Family if grep -q "${IPV4_ADDRESS}" /etc/dhcpcd.conf; then echo "::: Static IP already configured" else setDHCPCD ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" echo ":::" echo "::: Setting IP to ${IPV4_ADDRESS}. You may need to restart after the install is complete." echo ":::" fi elif [[ -f /etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE} ]];then # Fedora Family IFCFG_FILE=/etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE} if grep -q "${IPV4_ADDRESS}" "${IFCFG_FILE}"; then echo "::: Static IP already configured" else IPADDR=$(echo "${IPV4_ADDRESS}" | cut -f1 -d/) CIDR=$(echo "${IPV4_ADDRESS}" | cut -f2 -d/) # Backup existing interface configuration: cp "${IFCFG_FILE}" "${IFCFG_FILE}".pihole.orig # Build Interface configuration file: { echo "# Configured via Pi-Hole installer" echo "DEVICE=$PIHOLE_INTERFACE" echo "BOOTPROTO=none" echo "ONBOOT=yes" echo "IPADDR=$IPADDR" echo "PREFIX=$CIDR" echo "GATEWAY=$IPv4gw" echo "DNS1=$PIHOLE_DNS_1" echo "DNS2=$PIHOLE_DNS_2" echo "USERCTL=no" }> "${IFCFG_FILE}" ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" if [ -x "$(command -v nmcli)" ];then # Tell NetworkManager to read our new sysconfig file nmcli con load "${IFCFG_FILE}" > /dev/null fi echo ":::" echo "::: Setting IP to ${IPV4_ADDRESS}. You may need to restart after the install is complete." echo ":::" fi else echo "::: Warning: Unable to locate configuration file to set static IPv4 address!" exit 1 fi } valid_ip() { local ip=${1} local stat=1 if [[ ${ip} =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then OIFS=$IFS IFS='.' ip=(${ip}) IFS=${OIFS} [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \ && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]] stat=$? fi return ${stat} } setDNS() { DNSChooseCmd=(whiptail --separate-output --radiolist "Select Upstream DNS Provider. To use your own, select Custom." ${r} ${c} 6) DNSChooseOptions=(Google "" on OpenDNS "" off Level3 "" off Norton "" off Comodo "" off Custom "" off) DNSchoices=$("${DNSChooseCmd[@]}" "${DNSChooseOptions[@]}" 2>&1 >/dev/tty) if [[ $? = 0 ]];then case ${DNSchoices} in Google) echo "::: Using Google DNS servers." PIHOLE_DNS_1="8.8.8.8" PIHOLE_DNS_2="8.8.4.4" ;; OpenDNS) echo "::: Using OpenDNS servers." PIHOLE_DNS_1="208.67.222.222" PIHOLE_DNS_2="208.67.220.220" ;; Level3) echo "::: Using Level3 servers." PIHOLE_DNS_1="4.2.2.1" PIHOLE_DNS_2="4.2.2.2" ;; Norton) echo "::: Using Norton ConnectSafe servers." PIHOLE_DNS_1="199.85.126.10" PIHOLE_DNS_2="199.85.127.10" ;; Comodo) echo "::: Using Comodo Secure servers." PIHOLE_DNS_1="8.26.56.26" PIHOLE_DNS_2="8.20.247.20" ;; Custom) until [[ ${DNSSettingsCorrect} = True ]]; do strInvalid="Invalid" if [ ! ${PIHOLE_DNS_1} ]; then if [ ! ${PIHOLE_DNS_2} ]; then prePopulate="" else prePopulate=", ${PIHOLE_DNS_2}" fi elif [ ${PIHOLE_DNS_1} ] && [ ! ${PIHOLE_DNS_2} ]; then prePopulate="${PIHOLE_DNS_1}" elif [ ${PIHOLE_DNS_1} ] && [ ${PIHOLE_DNS_2} ]; then prePopulate="${PIHOLE_DNS_1}, ${PIHOLE_DNS_2}" fi piholeDNS=$(whiptail --backtitle "Specify Upstream DNS Provider(s)" --inputbox "Enter your desired upstream DNS provider(s), seperated by a comma.\n\nFor example '8.8.8.8, 8.8.4.4'" ${r} ${c} "${prePopulate}" 3>&1 1>&2 2>&3) if [[ $? = 0 ]]; then PIHOLE_DNS_1=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$1}') PIHOLE_DNS_2=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$2}') if ! valid_ip "${PIHOLE_DNS_1}" || [ ! "${PIHOLE_DNS_1}" ]; then PIHOLE_DNS_1=${strInvalid} fi if ! valid_ip "${PIHOLE_DNS_2}" && [ "${PIHOLE_DNS_2}" ]; then PIHOLE_DNS_2=${strInvalid} fi else echo "::: Cancel selected, exiting...." exit 1 fi if [[ ${PIHOLE_DNS_1} == "${strInvalid}" ]] || [[ ${PIHOLE_DNS_2} == "${strInvalid}" ]]; then whiptail --msgbox --backtitle "Invalid IP" --title "Invalid IP" "One or both entered IP addresses were invalid. Please try again.\n\n DNS Server 1: $PIHOLE_DNS_1\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c} if [[ ${PIHOLE_DNS_1} == "${strInvalid}" ]]; then PIHOLE_DNS_1="" fi if [[ ${PIHOLE_DNS_2} == "${strInvalid}" ]]; then PIHOLE_DNS_2="" fi DNSSettingsCorrect=False else if (whiptail --backtitle "Specify Upstream DNS Provider(s)" --title "Upstream DNS Provider(s)" --yesno "Are these settings correct?\n DNS Server 1: $PIHOLE_DNS_1\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c}); then DNSSettingsCorrect=True else # If the settings are wrong, the loop continues DNSSettingsCorrect=False fi fi done ;; esac else echo "::: Cancel selected. Exiting..." exit 1 fi } setLogging() { local LogToggleCommand local LogChooseOptions local LogChoices LogToggleCommand=(whiptail --separate-output --radiolist "Do you want to log queries?\n (Disabling will render graphs on the Admin page useless):" ${r} ${c} 6) LogChooseOptions=("On (Reccomended)" "" on Off "" off) LogChoices=$("${LogToggleCommand[@]}" "${LogChooseOptions[@]}" 2>&1 >/dev/tty) || (echo "::: Cancel selected. Exiting..." && exit 1) case ${LogChoices} in "On (Recommended)") echo "::: Logging On." QUERY_LOGGING=true ;; Off) echo "::: Logging Off." QUERY_LOGGING=false ;; esac } version_check_dnsmasq() { # Check if /etc/dnsmasq.conf is from pihole. If so replace with an original and install new in .d directory local dnsmasq_conf="/etc/dnsmasq.conf" local dnsmasq_conf_orig="/etc/dnsmasq.conf.orig" local dnsmasq_pihole_id_string="addn-hosts=/etc/pihole/gravity.list" local dnsmasq_original_config="/etc/.pihole/advanced/dnsmasq.conf.original" local dnsmasq_pihole_01_snippet="/etc/.pihole/advanced/01-pihole.conf" local dnsmasq_pihole_01_location="/etc/dnsmasq.d/01-pihole.conf" if [ -f ${dnsmasq_conf} ]; then echo -n "::: Existing dnsmasq.conf found..." if grep -q ${dnsmasq_pihole_id_string} ${dnsmasq_conf}; then echo " it is from a previous pi-hole install." echo -n "::: Backing up dnsmasq.conf to dnsmasq.conf.orig..." mv -f ${dnsmasq_conf} ${dnsmasq_conf_orig} echo " done." echo -n "::: Restoring default dnsmasq.conf..." cp ${dnsmasq_original_config} ${dnsmasq_conf} echo " done." else echo " it is not a pi-hole file, leaving alone!" fi else echo -n "::: No dnsmasq.conf found.. restoring default dnsmasq.conf..." cp ${dnsmasq_original_config} ${dnsmasq_conf} echo " done." fi echo -n "::: Copying 01-pihole.conf to /etc/dnsmasq.d/01-pihole.conf..." cp ${dnsmasq_pihole_01_snippet} ${dnsmasq_pihole_01_location} echo " done." sed -i "s/@INT@/$PIHOLE_INTERFACE/" ${dnsmasq_pihole_01_location} if [[ "${PIHOLE_DNS_1}" != "" ]]; then sed -i "s/@DNS1@/$PIHOLE_DNS_1/" ${dnsmasq_pihole_01_location} else sed -i '/^server=@DNS1@/d' ${dnsmasq_pihole_01_location} fi if [[ "${PIHOLE_DNS_2}" != "" ]]; then sed -i "s/@DNS2@/$PIHOLE_DNS_2/" ${dnsmasq_pihole_01_location} else sed -i '/^server=@DNS2@/d' ${dnsmasq_pihole_01_location} fi sed -i 's/^#conf-dir=\/etc\/dnsmasq.d$/conf-dir=\/etc\/dnsmasq.d/' ${dnsmasq_conf} if [[ "${QUERY_LOGGING}" == false ]] ; then #Disable Logging sed -i 's/^log-queries/#log-queries/' ${dnsmasq_pihole_01_location} else #Enable Logging sed -i 's/^#log-queries/log-queries/' ${dnsmasq_pihole_01_location} fi } remove_legacy_scripts() { #Tidy up /usr/local/bin directory if installing over previous install. oldFiles=( gravity chronometer whitelist blacklist piholeLogFlush updateDashboard uninstall setupLCD piholeDebug) for i in "${oldFiles[@]}"; do if [ -f "/usr/local/bin/$i.sh" ]; then rm /usr/local/bin/"$i".sh fi done } clean_existing() { # Clean an exiting installation to prepare for upgrade/reinstall # ${1} Directory to clean; ${2} Array of files to remove local clean_directory="${1}" local old_files=${2} for script in "${old_files[@]}"; do rm -f "${clean_directory}${script}.sh" done } installScripts() { # Install the scripts from repository to their various locations readonly install_dir="/opt/pihole/" echo ":::" echo -n "::: Installing scripts from ${PI_HOLE_LOCAL_REPO}..." # Clear out script files from Pi-hole scripts directory. clean_existing "${install_dir}" "${PI_HOLE_FILES}" # Install files from local core repository if is_repo "${PI_HOLE_LOCAL_REPO}"; then cd "${PI_HOLE_LOCAL_REPO}" install -o "${USER}" -Dm755 -t /opt/pihole/ gravity.sh install -o "${USER}" -Dm755 -t /opt/pihole/ ./advanced/Scripts/*.sh install -o "${USER}" -Dm755 -t /opt/pihole/ ./automated\ install/uninstall.sh install -o "${USER}" -Dm755 -t /usr/local/bin/ pihole install -Dm644 ./advanced/bash-completion/pihole /etc/bash_completion.d/pihole echo " done." else echo " *** ERROR: Local repo ${core_repo} not found, exiting." exit 1 fi } installConfigs() { # Install the configs from /etc/.pihole to their various locations echo ":::" echo "::: Installing configs..." version_check_dnsmasq if [ ! -d "/etc/lighttpd" ]; then mkdir /etc/lighttpd chown "${USER}":root /etc/lighttpd elif [ -f "/etc/lighttpd/lighttpd.conf" ]; then mv /etc/lighttpd/lighttpd.conf /etc/lighttpd/lighttpd.conf.orig fi cp /etc/.pihole/advanced/${LIGHTTPD_CFG} /etc/lighttpd/lighttpd.conf mkdir -p /var/run/lighttpd chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/run/lighttpd mkdir -p /var/cache/lighttpd/compress chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/compress mkdir -p /var/cache/lighttpd/uploads chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/uploads } stop_service() { # Stop service passed in as argument. # Can softfail, as process may not be installed when this is called echo ":::" echo -n "::: Stopping ${1} service..." if [ -x "$(command -v systemctl)" ]; then systemctl stop "${1}" &> /dev/null || true else service "${1}" stop &> /dev/null || true fi echo " done." } start_service() { # Start/Restart service passed in as argument # This should not fail, it's an error if it does echo ":::" echo -n "::: Starting ${1} service..." if [ -x "$(command -v systemctl)" ]; then systemctl restart "${1}" &> /dev/null else service "${1}" restart &> /dev/null fi echo " done." } enable_service() { # Enable service so that it will start with next reboot echo ":::" echo -n "::: Enabling ${1} service to start on reboot..." if [ -x "$(command -v systemctl)" ]; then systemctl enable "${1}" &> /dev/null else update-rc.d "${1}" defaults &> /dev/null fi echo " done." } update_pacakge_cache() { #Running apt-get update/upgrade with minimal output can cause some issues with #requiring user input (e.g password for phpmyadmin see #218) #Check to see if apt-get update has already been run today #it needs to have been run at least once on new installs! timestamp=$(stat -c %Y ${PKG_CACHE}) timestampAsDate=$(date -d @"${timestamp}" "+%b %e") today=$(date "+%b %e") if [ ! "${today}" == "${timestampAsDate}" ]; then #update package lists echo ":::" echo -n "::: ${PKG_MANAGER} update has not been run today. Running now..." ${UPDATE_PKG_CACHE} &> /dev/null echo " done!" fi } notify_package_updates_available() { # Let user know if they have outdated packages on their system and # advise them to run a package update at soonest possible. echo ":::" echo -n "::: Checking ${PKG_MANAGER} for upgraded packages...." updatesToInstall=$(eval "${PKG_COUNT}") echo " done!" echo ":::" if [[ ${updatesToInstall} -eq "0" ]]; then echo "::: Your system is up to date! Continuing with Pi-hole installation..." else echo "::: There are ${updatesToInstall} updates available for your system!" echo "::: We recommend you run '${PKG_UPDATE}' after installing Pi-Hole! " echo ":::" fi } install_dependent_packages() { # Install packages passed in via argument array # No spinner - conflicts with set -e declare -a argArray1=("${!1}") if command -v debconf-apt-progress &> /dev/null; then debconf-apt-progress -- ${PKG_INSTALL} "${argArray1[@]}" else for i in "${argArray1[@]}"; do echo -n "::: Checking for $i..." package_check_install "${i}" &> /dev/null echo " installed!" done fi } CreateLogFile() { # Create logfiles if necessary echo ":::" echo -n "::: Creating log file and changing owner to dnsmasq..." if [ ! -f /var/log/pihole.log ]; then touch /var/log/pihole.log chmod 644 /var/log/pihole.log chown "${DNSMASQ_USER}":root /var/log/pihole.log echo " done!" else echo " already exists!" fi } installPiholeWeb() { # Install the web interface echo ":::" echo "::: Installing pihole custom index page..." if [ -d "/var/www/html/pihole" ]; then if [ -f "/var/www/html/pihole/index.html" ]; then echo "::: Existing index.html detected, not overwriting" else echo -n "::: index.html missing, replacing... " cp /etc/.pihole/advanced/index.html /var/www/html/pihole/ echo " done!" fi if [ -f "/var/www/html/pihole/index.js" ]; then echo "::: Existing index.js detected, not overwriting" else echo -n "::: index.js missing, replacing... " cp /etc/.pihole/advanced/index.js /var/www/html/pihole/ echo " done!" fi else mkdir /var/www/html/pihole if [ -f /var/www/html/index.lighttpd.html ]; then mv /var/www/html/index.lighttpd.html /var/www/html/index.lighttpd.orig else printf "\n:::\tNo default index.lighttpd.html file found... not backing up" fi cp /etc/.pihole/advanced/index.* /var/www/html/pihole/. echo " done!" fi # Install Sudoer file echo ":::" echo -n "::: Installing sudoer file..." mkdir -p /etc/sudoers.d/ cp /etc/.pihole/advanced/pihole.sudo /etc/sudoers.d/pihole chmod 0440 /etc/sudoers.d/pihole echo " done!" } installCron() { # Install the cron job echo ":::" echo -n "::: Installing latest Cron script..." cp /etc/.pihole/advanced/pihole.cron /etc/cron.d/pihole echo " done!" } runGravity() { # Run gravity.sh to build blacklists echo ":::" echo "::: Preparing to run gravity.sh to refresh hosts..." if ls /etc/pihole/list* 1> /dev/null 2>&1; then echo "::: Cleaning up previous install (preserving whitelist/blacklist)" rm /etc/pihole/list.* fi echo "::: Running gravity.sh" /opt/pihole/gravity.sh } create_pihole_user() { # Check if user pihole exists and create if not echo "::: Checking if user 'pihole' exists..." id -u pihole &> /dev/null && echo "::: User 'pihole' already exists" || (echo "::: User 'pihole' doesn't exist. Creating..." && useradd -r -s /usr/sbin/nologin pihole) } configureFirewall() { # Allow HTTP and DNS traffic if [ -x "$(command -v firewall-cmd)" ]; then firewall-cmd --state &> /dev/null && ( echo "::: Configuring firewalld for httpd and dnsmasq.." && firewall-cmd --permanent --add-port=80/tcp && firewall-cmd --permanent --add-port=53/tcp \ && firewall-cmd --permanent --add-port=53/udp && firewall-cmd --reload) || echo "::: FirewallD not enabled" elif [ -x "$(command -v iptables)" ]; then echo "::: Configuring iptables for httpd and dnsmasq.." iptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT iptables -A INPUT -p tcp -m tcp --dport 53 -j ACCEPT iptables -A INPUT -p udp -m udp --dport 53 -j ACCEPT else echo "::: No firewall detected.. skipping firewall configuration." fi } finalExports() { # Update variables in setupVars.conf file if [ -e "${setupVars}" ]; then sed -i.update.bak '/PIHOLE_INTERFACE/d;/IPV4_ADDRESS/d;/IPV6_ADDRESS/d;/PIHOLE_DNS_1/d;/PIHOLE_DNS_2/d;/QUERY_LOGGING/d;' "${setupVars}" fi { echo "PIHOLE_INTERFACE=${PIHOLE_INTERFACE}" echo "IPV4_ADDRESS=${IPV4_ADDRESS}" echo "IPV6_ADDRESS=${IPV6_ADDRESS}" echo "PIHOLE_DNS_1=${PIHOLE_DNS_1}" echo "PIHOLE_DNS_2=${PIHOLE_DNS_2}" echo "QUERY_LOGGING=${QUERY_LOGGING}" }>> "${setupVars}" } installPihole() { # Install base files and web interface create_pihole_user if [ ! -d "/var/www/html" ]; then mkdir -p /var/www/html fi chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/www/html chmod 775 /var/www/html usermod -a -G ${LIGHTTPD_GROUP} pihole if [ -x "$(command -v lighty-enable-mod)" ]; then lighty-enable-mod fastcgi fastcgi-php > /dev/null || true else printf "\n:::\tWarning: 'lighty-enable-mod' utility not found. Please ensure fastcgi is enabled if you experience issues.\n" fi installScripts installConfigs CreateLogFile configureSelinux installPiholeWeb installCron configureFirewall finalExports runGravity } accountForRefactor() { # At some point in the future this list can be pruned, for now we'll need it to ensure updates don't break. # Refactoring of install script has changed the name of a couple of variables. Sort them out here. sed -i 's/piholeInterface/PIHOLE_INTERFACE/g' ${setupVars} sed -i 's/IPv4_address/IPV4_ADDRESS/g' ${setupVars} sed -i 's/IPv4addr/IPV4_ADDRESS/g' ${setupVars} sed -i 's/IPv6_address/IPV6_ADDRESS/g' ${setupVars} sed -i 's/piholeIPv6/IPV6_ADDRESS/g' ${setupVars} sed -i 's/piholeDNS1/PIHOLE_DNS_1/g' ${setupVars} sed -i 's/piholeDNS2/PIHOLE_DNS_2/g' ${setupVars} } updatePihole() { accountForRefactor # Source ${setupVars} for use in the rest of the functions. . ${setupVars} # Install base files and web interface installScripts installConfigs CreateLogFile configureSelinux installPiholeWeb installCron configureFirewall finalExports #re-export setupVars.conf to account for any new vars added in new versions runGravity } configureSelinux() { if [ -x "$(command -v getenforce)" ]; then printf "\n::: SELinux Detected\n" printf ":::\tChecking for SELinux policy development packages..." package_check_install "selinux-policy-devel" > /dev/null echo " installed!" printf ":::\tEnabling httpd server side includes (SSI).. " setsebool -P httpd_ssi_exec on &> /dev/null && echo "Success" || echo "SELinux not enabled" printf "\n:::\tCompiling Pi-Hole SELinux policy..\n" if ! [ -x "$(command -v systemctl)" ]; then sed -i.bak '/systemd/d' /etc/.pihole/advanced/selinux/pihole.te fi checkmodule -M -m -o /etc/pihole/pihole.mod /etc/.pihole/advanced/selinux/pihole.te semodule_package -o /etc/pihole/pihole.pp -m /etc/pihole/pihole.mod semodule -i /etc/pihole/pihole.pp rm -f /etc/pihole/pihole.mod semodule -l | grep pihole &> /dev/null && echo "::: Installed Pi-Hole SELinux policy" || echo "::: Warning: Pi-Hole SELinux policy did not install." fi } displayFinalMessage() { if (( ${#1} > 0 )) ; then # Final completion message to user whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: IPv4: ${IPV4_ADDRESS%/*} IPv6: ${IPV6_ADDRESS} If you set a new IP address, you should restart the Pi. The install log is in /etc/pihole. View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin The currently set password is ${1}" ${r} ${c} else whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: IPv4: ${IPV4_ADDRESS%/*} IPv6: ${IPV6_ADDRESS} If you set a new IP address, you should restart the Pi. The install log is in /etc/pihole. View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin" ${r} ${c} fi } update_dialogs() { # reconfigure if [ "${reconfigure}" = true ]; then opt1a="Repair" opt1b="This will retain existing settings" strAdd="You will remain on the same version" else opt1a="Update" opt1b="This will retain existing settings." strAdd="You will be updated to the latest version." fi opt2a="Reconfigure" opt2b="This will allow you to enter new settings" UpdateCmd=$(whiptail --title "Existing Install Detected!" --menu "\n\nWe have detected an existing install.\n\nPlease choose from the following options: \n($strAdd)" ${r} ${c} 2 \ "${opt1a}" "${opt1b}" \ "${opt2a}" "${opt2b}" 3>&2 2>&1 1>&3) if [[ $? = 0 ]];then case ${UpdateCmd} in ${opt1a}) echo "::: ${opt1a} option selected." useUpdateVars=true ;; ${opt2a}) echo "::: ${opt2a} option selected" useUpdateVars=false ;; esac else echo "::: Cancel selected. Exiting..." exit 1 fi } main() { # Check arguments for the undocumented flags for var in "$@"; do case "$var" in "--reconfigure" ) reconfigure=true;; "--i_do_not_follow_recommendations" ) skipSpaceCheck=false;; "--unattended" ) runUnattended=true;; esac done if [[ -f ${setupVars} ]]; then if [[ "${runUnattended}" == true ]]; then echo "::: --unattended passed to install script, no whiptail dialogs will be displayed" useUpdateVars=true else update_dialogs fi fi # Start the installer # Verify there is enough disk space for the install if [[ "${skipSpaceCheck}" == true ]]; then echo "::: --i_do_not_follow_recommendations passed to script, skipping free disk space verification!" else verifyFreeDiskSpace fi # Update package cache update_pacakge_cache # Notify user of package availability notify_package_updates_available # Install packages used by this installation script install_dependent_packages INSTALLER_DEPS[@] if [[ "${reconfigure}" == true ]]; then echo "::: --reconfigure passed to install script. Not downloading/updating local repos" else # Get Git files for Core and Admin getGitFiles ${PI_HOLE_LOCAL_REPO} ${piholeGitUrl} getGitFiles ${webInterfaceDir} ${webInterfaceGitUrl} fi if [[ ${useUpdateVars} == false ]]; then # Display welcome dialogs welcomeDialogs # Create directory for Pi-hole storage mkdir -p /etc/pihole/ # Remove legacy scripts from previous storage location remove_legacy_scripts # Stop resolver and webserver while installing proceses stop_service dnsmasq stop_service lighttpd # Determine available interfaces get_available_interfaces # Find interfaces and let the user choose one chooseInterface # Let the user decide if they want to block ads over IPv4 and/or IPv6 use4andor6 # Decide what upstream DNS Servers to use setDNS # Let the user decide if they want query logging enabled... setLogging # Install packages used by the Pi-hole install_dependent_packages PIHOLE_DEPS[@] # Install and log everything to a file installPihole | tee ${tmpLog} else # update packages used by the Pi-hole install_dependent_packages PIHOLE_DEPS[@] updatePihole | tee ${tmpLog} fi # Move the log file into /etc/pihole for storage mv ${tmpLog} ${instalLogLoc} # Add password to web UI if there is none pw="" if [[ $(grep 'WEBPASSWORD' -c /etc/pihole/setupVars.conf) == 0 ]] ; then pw=$(tr -dc _A-Z-a-z-0-9 < /dev/urandom | head -c 8) pihole -a -p ${pw} fi if [[ "${useUpdateVars}" == false ]]; then displayFinalMessage ${pw} fi echo "::: Restarting services..." # Start services start_service dnsmasq enable_service dnsmasq start_service lighttpd enable_service lighttpd echo "::: done." echo ":::" if [[ "${useUpdateVars}" == false ]]; then echo "::: Installation Complete! Configure your devices to use the Pi-hole as their DNS server using:" echo "::: ${IPV4_ADDRESS%/*}" echo "::: ${IPV6_ADDRESS}" echo ":::" echo "::: If you set a new IP address, you should restart the Pi." echo "::: View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin" else echo "::: Update complete!" fi if (( ${#pw} > 0 )) ; then echo ":::" echo "::: Note: As security measure a password has been installed for your web interface" echo "::: The currently set password is" echo "::: ${pw}" echo ":::" echo "::: You can always change it using" echo "::: pihole -a -p new_password" fi echo ":::" echo "::: The install log is located at: /etc/pihole/install.log" } if [[ "${PH_TEST}" != true ]] ; then main "$@" fi \ No newline at end of file From 7f3b0030ea478e4c5867b8e173a0f5b68aec8688 Mon Sep 17 00:00:00 2001 From: Dan Schaper Date: Tue, 20 Dec 2016 16:47:43 -0800 Subject: [PATCH 27/89] LF Signed-off-by: Dan Schaper --- automated install/basic-install.sh | 1144 +++++++++++++++++++++++++++- 1 file changed, 1143 insertions(+), 1 deletion(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index c64c3864..0c66094f 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -1 +1,1143 @@ -#!/usr/bin/env bash # Pi-hole: A black hole for Internet advertisements # (c) 2015, 2016 by Jacob Salmela # Network-wide ad blocking via your Raspberry Pi # http://pi-hole.net # Installs Pi-hole # # Pi-hole is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # pi-hole.net/donate # # Install with this command (from your Pi): # # curl -L install.pi-hole.net | bash set -e ######## VARIABLES ######### tmpLog=/tmp/pihole-install.log instalLogLoc=/etc/pihole/install.log setupVars=/etc/pihole/setupVars.conf webInterfaceGitUrl="https://github.com/pi-hole/AdminLTE.git" webInterfaceDir="/var/www/html/admin" piholeGitUrl="https://github.com/pi-hole/pi-hole.git" PI_HOLE_LOCAL_REPO="/etc/.pihole" PI_HOLE_FILES=(chronometer list piholeDebug piholeLogFlush setupLCD update version) useUpdateVars=false IPV4_ADDRESS="" IPV6_ADDRESS="" QUERY_LOGGING=true # Find the rows and columns will default to 80x24 is it can not be detected screen_size=$(stty size 2>/dev/null || echo 24 80) rows=$(echo $screen_size | awk '{print $1}') columns=$(echo $screen_size | awk '{print $2}') # Divide by two so the dialogs take up half of the screen, which looks nice. r=$(( rows / 2 )) c=$(( columns / 2 )) # Unless the screen is tiny r=$(( r < 20 ? 20 : r )) c=$(( c < 70 ? 70 : c )) ######## Undocumented Flags. Shhh ######## skipSpaceCheck=false reconfigure=false runUnattended=false ######## FIRST CHECK ######## # Must be root to install echo ":::" if [[ ${EUID} -eq 0 ]]; then echo "::: You are root." else echo "::: Script called with non-root privileges. The Pi-hole installs server packages and configures" echo "::: system networking, it requires elevated rights. Please check the contents of the script for" echo "::: any concerns with this requirement. Please be sure to download this script from a trusted source." echo ":::" echo "::: Detecting the presence of the sudo utility for continuation of this install..." if [ -x "$(command -v sudo)" ]; then echo "::: Utility sudo located." exec curl -sSL https://install.pi-hole.net | sudo bash "$@" exit $? else echo "::: sudo is needed for the Web interface to run pihole commands. Please run this script as root and it will be automatically installed." exit 1 fi fi # Compatibility if [[ $(command -v apt-get) ]]; then #Debian Family ############################################# PKG_MANAGER="apt-get" PKG_CACHE="/var/lib/apt/lists/" UPDATE_PKG_CACHE="${PKG_MANAGER} update" PKG_UPDATE="${PKG_MANAGER} upgrade" PKG_INSTALL="${PKG_MANAGER} --yes --fix-missing install" # grep -c will return 1 retVal on 0 matches, block this throwing the set -e with an OR TRUE PKG_COUNT="${PKG_MANAGER} -s -o Debug::NoLocking=true upgrade | grep -c ^Inst || true" # ######################################### # fixes for dependancy differences # Debian 7 doesn't have iproute2 use iproute ${PKG_MANAGER} install --dry-run iproute2 > /dev/null 2>&1 && IPROUTE_PKG="iproute2" || IPROUTE_PKG="iproute" # Prefer the php metapackage if it's there, fall back on the php5 pacakges ${PKG_MANAGER} install --dry-run php > /dev/null 2>&1 && phpVer="php" || phpVer="php5" # ######################################### INSTALLER_DEPS=(apt-utils debconf dhcpcd5 git whiptail) PIHOLE_DEPS=(bc cron curl dnsmasq dnsutils ${IPROUTE_PKG} iputils-ping lighttpd lsof netcat ${phpVer}-common ${phpVer}-cgi sudo unzip wget) LIGHTTPD_USER="www-data" LIGHTTPD_GROUP="www-data" LIGHTTPD_CFG="lighttpd.conf.debian" DNSMASQ_USER="dnsmasq" package_check_install() { dpkg-query -W -f='${Status}' "${1}" 2>/dev/null | grep -c "ok installed" || ${PKG_INSTALL} "${1}" } elif [ $(command -v rpm) ]; then # Fedora Family if [ $(command -v dnf) ]; then PKG_MANAGER="dnf" else PKG_MANAGER="yum" fi PKG_CACHE="/var/cache/${PKG_MANAGER}" UPDATE_PKG_CACHE="${PKG_MANAGER} check-update" PKG_UPDATE="${PKG_MANAGER} update -y" PKG_INSTALL="${PKG_MANAGER} install -y" PKG_COUNT="${PKG_MANAGER} check-update | egrep '(.i686|.x86|.noarch|.arm|.src)' | wc -l" INSTALLER_DEPS=(git iproute net-tools newt procps-ng) PIHOLE_DEPS=(bc bind-utils cronie curl dnsmasq epel-release findutils lighttpd lighttpd-fastcgi nmap-ncat php php-common php-cli sudo unzip wget) if grep -q 'Fedora' /etc/redhat-release; then remove_deps=(epel-release); PIHOLE_DEPS=( ${PIHOLE_DEPS[@]/$remove_deps} ); fi LIGHTTPD_USER="lighttpd" LIGHTTPD_GROUP="lighttpd" LIGHTTPD_CFG="lighttpd.conf.fedora" DNSMASQ_USER="nobody" package_check_install() { rpm -qa | grep ^"${1}"- > /dev/null || ${PKG_INSTALL} "${1}" } else echo "OS distribution not supported" exit fi ####### FUNCTIONS ########## is_repo() { # Use git to check if directory is currently under VCS, return the value local directory="${1}" git -C "${directory}" status --short &> /dev/null return } make_repo() { local directory="${1}" local remoteRepo="${2}" # Remove the non-repod interface and clone the interface echo -n "::: Cloning $remoteRepo into $directory..." rm -rf "${directory}" git clone -q --depth 1 "${remoteRepo}" "${directory}" &> /dev/null echo " done!" } update_repo() { local directory="${1}" # Pull the latest commits echo -n "::: Updating repo in $1..." cd "${directory}" || exit 1 git stash -q &> /dev/null git pull -q &> /dev/null echo " done!" } getGitFiles() { # Setup git repos for directory and repository passed # as arguments 1 and 2 local directory="${1}" local remoteRepo="${2}" echo ":::" echo "::: Checking for existing repository..." if is_repo "${directory}"; then update_repo "${directory}" else make_repo "${directory}" "${remoteRepo}" fi } find_IPv4_information() { # Find IP used to route to outside world IPv4dev=$(ip route get 8.8.8.8 | awk '{for(i=1;i<=NF;i++)if($i~/dev/)print $(i+1)}') IPV4_ADDRESS=$(ip -o -f inet addr show dev "$IPv4dev" | awk '{print $4}' | awk 'END {print}') IPv4gw=$(ip route get 8.8.8.8 | awk '{print $3}') } get_available_interfaces() { # Get available interfaces. Consider only getting UP interfaces in the future, and leaving DOWN interfaces out of list. availableInterfaces=$(ip -o link | awk '{print $2}' | grep -v "lo" | cut -d':' -f1 | cut -d'@' -f1) } welcomeDialogs() { # Display the welcome dialog whiptail --msgbox --backtitle "Welcome" --title "Pi-hole automated installer" "\n\nThis installer will transform your device into a network-wide ad blocker!" ${r} ${c} # Support for a part-time dev whiptail --msgbox --backtitle "Plea" --title "Free and open source" "\n\nThe Pi-hole is free, but powered by your donations: http://pi-hole.net/donate" ${r} ${c} # Explain the need for a static address whiptail --msgbox --backtitle "Initiating network interface" --title "Static IP Needed" "\n\nThe Pi-hole is a SERVER so it needs a STATIC IP ADDRESS to function properly. In the next section, you can choose to use your current network settings (DHCP) or to manually edit them." ${r} ${c} } verifyFreeDiskSpace() { # 50MB is the minimum space needed (45MB install (includes web admin bootstrap/jquery libraries etc) + 5MB one day of logs.) # - Fourdee: Local ensures the variable is only created, and accessible within this function/void. Generally considered a "good" coding practice for non-global variables. echo "::: Verifying free disk space..." local required_free_kilobytes=51200 local existing_free_kilobytes=$(df -Pk | grep -m1 '\/$' | awk '{print $4}') # - Unknown free disk space , not a integer if ! [[ "${existing_free_kilobytes}" =~ ^([0-9])+$ ]]; then echo "::: Unknown free disk space!" echo "::: We were unable to determine available free disk space on this system." echo "::: You may override this check and force the installation, however, it is not recommended" echo "::: To do so, pass the argument '--i_do_not_follow_recommendations' to the install script" echo "::: eg. curl -L https://install.pi-hole.net | bash /dev/stdin --i_do_not_follow_recommendations" exit 1 # - Insufficient free disk space elif [[ ${existing_free_kilobytes} -lt ${required_free_kilobytes} ]]; then echo "::: Insufficient Disk Space!" echo "::: Your system appears to be low on disk space. pi-hole recommends a minimum of $required_free_kilobytes KiloBytes." echo "::: You only have ${existing_free_kilobytes} KiloBytes free." echo "::: If this is a new install you may need to expand your disk." echo "::: Try running 'sudo raspi-config', and choose the 'expand file system option'" echo "::: After rebooting, run this installation again. (curl -L https://install.pi-hole.net | bash)" echo "Insufficient free space, exiting..." exit 1 fi } chooseInterface() { # Turn the available interfaces into an array so it can be used with a whiptail dialog local interfacesArray=() # Number of available interfaces local interfaceCount # Whiptail variable storage local chooseInterfaceCmd # Temporary Whiptail options storage local chooseInterfaceOptions # Loop sentinel variable local firstLoop=1 while read -r line; do mode="OFF" if [[ ${firstLoop} -eq 1 ]]; then firstLoop=0 mode="ON" fi interfacesArray+=("${line}" "available" "${mode}") done <<< "${availableInterfaces}" # Find out how many interfaces are available to choose from interfaceCount=$(echo "${availableInterfaces}" | wc -l) chooseInterfaceCmd=(whiptail --separate-output --radiolist "Choose An Interface (press space to select)" ${r} ${c} ${interfaceCount}) chooseInterfaceOptions=$("${chooseInterfaceCmd[@]}" "${interfacesArray[@]}" 2>&1 >/dev/tty) if [[ $? = 0 ]]; then for desiredInterface in ${chooseInterfaceOptions}; do PIHOLE_INTERFACE=${desiredInterface} echo "::: Using interface: $PIHOLE_INTERFACE" done else echo "::: Cancel selected, exiting...." exit 1 fi } useIPv6dialog() { # Show the IPv6 address used for blocking IPV6_ADDRESS=$(ip -6 route get 2001:4860:4860::8888 | awk -F " " '{ for(i=1;i<=NF;i++) if ($i == "src") print $(i+1) }') whiptail --msgbox --backtitle "IPv6..." --title "IPv6 Supported" "$IPV6_ADDRESS will be used to block ads." ${r} ${c} } use4andor6() { local useIPv4 local useIPv6 # Let use select IPv4 and/or IPv6 cmd=(whiptail --separate-output --checklist "Select Protocols (press space to select)" ${r} ${c} 2) options=(IPv4 "Block ads over IPv4" on IPv6 "Block ads over IPv6" on) choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty) if [[ $? = 0 ]];then for choice in ${choices} do case ${choice} in IPv4 ) useIPv4=true;; IPv6 ) useIPv6=true;; esac done if [[ ${useIPv4} ]]; then find_IPv4_information getStaticIPv4Settings setStaticIPv4 fi if [[ ${useIPv6} ]]; then useIPv6dialog fi echo "::: IPv4 address: ${IPV4_ADDRESS}" echo "::: IPv6 address: ${IPV6_ADDRESS}" if [ ! ${useIPv4} ] && [ ! ${useIPv6} ]; then echo "::: Cannot continue, neither IPv4 or IPv6 selected" echo "::: Exiting" exit 1 fi else echo "::: Cancel selected. Exiting..." exit 1 fi } getStaticIPv4Settings() { # Ask if the user wants to use DHCP settings as their static IP if (whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Do you want to use your current network settings as a static address? IP address: ${IPV4_ADDRESS} Gateway: ${IPv4gw}" ${r} ${c}); then # If they choose yes, let the user know that the IP address will not be available via DHCP and may cause a conflict. whiptail --msgbox --backtitle "IP information" --title "FYI: IP Conflict" "It is possible your router could still try to assign this IP to a device, which would cause a conflict. But in most cases the router is smart enough to not do that. If you are worried, either manually set the address, or modify the DHCP reservation pool so it does not include the IP you want. It is also possible to use a DHCP reservation, but if you are going to do that, you might as well set a static address." ${r} ${c} # Nothing else to do since the variables are already set above else # Otherwise, we need to ask the user to input their desired settings. # Start by getting the IPv4 address (pre-filling it with info gathered from DHCP) # Start a loop to let the user enter their information with the chance to go back and edit it if necessary until [[ ${ipSettingsCorrect} = True ]]; do # Ask for the IPv4 address IPV4_ADDRESS=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 address" --inputbox "Enter your desired IPv4 address" ${r} ${c} "${IPV4_ADDRESS}" 3>&1 1>&2 2>&3) if [[ $? = 0 ]]; then echo "::: Your static IPv4 address: ${IPV4_ADDRESS}" # Ask for the gateway IPv4gw=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 gateway (router)" --inputbox "Enter your desired IPv4 default gateway" ${r} ${c} "${IPv4gw}" 3>&1 1>&2 2>&3) if [[ $? = 0 ]]; then echo "::: Your static IPv4 gateway: ${IPv4gw}" # Give the user a chance to review their settings before moving on if (whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Are these settings correct? IP address: ${IPV4_ADDRESS} Gateway: ${IPv4gw}" ${r} ${c}); then # After that's done, the loop ends and we move on ipSettingsCorrect=True else # If the settings are wrong, the loop continues ipSettingsCorrect=False fi else # Cancelling gateway settings window ipSettingsCorrect=False echo "::: Cancel selected. Exiting..." exit 1 fi else # Cancelling IPv4 settings window ipSettingsCorrect=False echo "::: Cancel selected. Exiting..." exit 1 fi done # End the if statement for DHCP vs. static fi } setDHCPCD() { # Append these lines to dhcpcd.conf to enable a static IP echo "## interface ${PIHOLE_INTERFACE} static ip_address=${IPV4_ADDRESS} static routers=${IPv4gw} static domain_name_servers=${IPv4gw}" | tee -a /etc/dhcpcd.conf >/dev/null } setStaticIPv4() { local IFCFG_FILE local IPADDR local CIDR if [[ -f /etc/dhcpcd.conf ]]; then # Debian Family if grep -q "${IPV4_ADDRESS}" /etc/dhcpcd.conf; then echo "::: Static IP already configured" else setDHCPCD ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" echo ":::" echo "::: Setting IP to ${IPV4_ADDRESS}. You may need to restart after the install is complete." echo ":::" fi elif [[ -f /etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE} ]];then # Fedora Family IFCFG_FILE=/etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE} if grep -q "${IPV4_ADDRESS}" "${IFCFG_FILE}"; then echo "::: Static IP already configured" else IPADDR=$(echo "${IPV4_ADDRESS}" | cut -f1 -d/) CIDR=$(echo "${IPV4_ADDRESS}" | cut -f2 -d/) # Backup existing interface configuration: cp "${IFCFG_FILE}" "${IFCFG_FILE}".pihole.orig # Build Interface configuration file: { echo "# Configured via Pi-Hole installer" echo "DEVICE=$PIHOLE_INTERFACE" echo "BOOTPROTO=none" echo "ONBOOT=yes" echo "IPADDR=$IPADDR" echo "PREFIX=$CIDR" echo "GATEWAY=$IPv4gw" echo "DNS1=$PIHOLE_DNS_1" echo "DNS2=$PIHOLE_DNS_2" echo "USERCTL=no" }> "${IFCFG_FILE}" ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" if [ -x "$(command -v nmcli)" ];then # Tell NetworkManager to read our new sysconfig file nmcli con load "${IFCFG_FILE}" > /dev/null fi echo ":::" echo "::: Setting IP to ${IPV4_ADDRESS}. You may need to restart after the install is complete." echo ":::" fi else echo "::: Warning: Unable to locate configuration file to set static IPv4 address!" exit 1 fi } valid_ip() { local ip=${1} local stat=1 if [[ ${ip} =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then OIFS=$IFS IFS='.' ip=(${ip}) IFS=${OIFS} [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \ && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]] stat=$? fi return ${stat} } setDNS() { DNSChooseCmd=(whiptail --separate-output --radiolist "Select Upstream DNS Provider. To use your own, select Custom." ${r} ${c} 6) DNSChooseOptions=(Google "" on OpenDNS "" off Level3 "" off Norton "" off Comodo "" off Custom "" off) DNSchoices=$("${DNSChooseCmd[@]}" "${DNSChooseOptions[@]}" 2>&1 >/dev/tty) if [[ $? = 0 ]];then case ${DNSchoices} in Google) echo "::: Using Google DNS servers." PIHOLE_DNS_1="8.8.8.8" PIHOLE_DNS_2="8.8.4.4" ;; OpenDNS) echo "::: Using OpenDNS servers." PIHOLE_DNS_1="208.67.222.222" PIHOLE_DNS_2="208.67.220.220" ;; Level3) echo "::: Using Level3 servers." PIHOLE_DNS_1="4.2.2.1" PIHOLE_DNS_2="4.2.2.2" ;; Norton) echo "::: Using Norton ConnectSafe servers." PIHOLE_DNS_1="199.85.126.10" PIHOLE_DNS_2="199.85.127.10" ;; Comodo) echo "::: Using Comodo Secure servers." PIHOLE_DNS_1="8.26.56.26" PIHOLE_DNS_2="8.20.247.20" ;; Custom) until [[ ${DNSSettingsCorrect} = True ]]; do strInvalid="Invalid" if [ ! ${PIHOLE_DNS_1} ]; then if [ ! ${PIHOLE_DNS_2} ]; then prePopulate="" else prePopulate=", ${PIHOLE_DNS_2}" fi elif [ ${PIHOLE_DNS_1} ] && [ ! ${PIHOLE_DNS_2} ]; then prePopulate="${PIHOLE_DNS_1}" elif [ ${PIHOLE_DNS_1} ] && [ ${PIHOLE_DNS_2} ]; then prePopulate="${PIHOLE_DNS_1}, ${PIHOLE_DNS_2}" fi piholeDNS=$(whiptail --backtitle "Specify Upstream DNS Provider(s)" --inputbox "Enter your desired upstream DNS provider(s), seperated by a comma.\n\nFor example '8.8.8.8, 8.8.4.4'" ${r} ${c} "${prePopulate}" 3>&1 1>&2 2>&3) if [[ $? = 0 ]]; then PIHOLE_DNS_1=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$1}') PIHOLE_DNS_2=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$2}') if ! valid_ip "${PIHOLE_DNS_1}" || [ ! "${PIHOLE_DNS_1}" ]; then PIHOLE_DNS_1=${strInvalid} fi if ! valid_ip "${PIHOLE_DNS_2}" && [ "${PIHOLE_DNS_2}" ]; then PIHOLE_DNS_2=${strInvalid} fi else echo "::: Cancel selected, exiting...." exit 1 fi if [[ ${PIHOLE_DNS_1} == "${strInvalid}" ]] || [[ ${PIHOLE_DNS_2} == "${strInvalid}" ]]; then whiptail --msgbox --backtitle "Invalid IP" --title "Invalid IP" "One or both entered IP addresses were invalid. Please try again.\n\n DNS Server 1: $PIHOLE_DNS_1\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c} if [[ ${PIHOLE_DNS_1} == "${strInvalid}" ]]; then PIHOLE_DNS_1="" fi if [[ ${PIHOLE_DNS_2} == "${strInvalid}" ]]; then PIHOLE_DNS_2="" fi DNSSettingsCorrect=False else if (whiptail --backtitle "Specify Upstream DNS Provider(s)" --title "Upstream DNS Provider(s)" --yesno "Are these settings correct?\n DNS Server 1: $PIHOLE_DNS_1\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c}); then DNSSettingsCorrect=True else # If the settings are wrong, the loop continues DNSSettingsCorrect=False fi fi done ;; esac else echo "::: Cancel selected. Exiting..." exit 1 fi } setLogging() { local LogToggleCommand local LogChooseOptions local LogChoices LogToggleCommand=(whiptail --separate-output --radiolist "Do you want to log queries?\n (Disabling will render graphs on the Admin page useless):" ${r} ${c} 6) LogChooseOptions=("On (Reccomended)" "" on Off "" off) LogChoices=$("${LogToggleCommand[@]}" "${LogChooseOptions[@]}" 2>&1 >/dev/tty) || (echo "::: Cancel selected. Exiting..." && exit 1) case ${LogChoices} in "On (Recommended)") echo "::: Logging On." QUERY_LOGGING=true ;; Off) echo "::: Logging Off." QUERY_LOGGING=false ;; esac } version_check_dnsmasq() { # Check if /etc/dnsmasq.conf is from pihole. If so replace with an original and install new in .d directory local dnsmasq_conf="/etc/dnsmasq.conf" local dnsmasq_conf_orig="/etc/dnsmasq.conf.orig" local dnsmasq_pihole_id_string="addn-hosts=/etc/pihole/gravity.list" local dnsmasq_original_config="/etc/.pihole/advanced/dnsmasq.conf.original" local dnsmasq_pihole_01_snippet="/etc/.pihole/advanced/01-pihole.conf" local dnsmasq_pihole_01_location="/etc/dnsmasq.d/01-pihole.conf" if [ -f ${dnsmasq_conf} ]; then echo -n "::: Existing dnsmasq.conf found..." if grep -q ${dnsmasq_pihole_id_string} ${dnsmasq_conf}; then echo " it is from a previous pi-hole install." echo -n "::: Backing up dnsmasq.conf to dnsmasq.conf.orig..." mv -f ${dnsmasq_conf} ${dnsmasq_conf_orig} echo " done." echo -n "::: Restoring default dnsmasq.conf..." cp ${dnsmasq_original_config} ${dnsmasq_conf} echo " done." else echo " it is not a pi-hole file, leaving alone!" fi else echo -n "::: No dnsmasq.conf found.. restoring default dnsmasq.conf..." cp ${dnsmasq_original_config} ${dnsmasq_conf} echo " done." fi echo -n "::: Copying 01-pihole.conf to /etc/dnsmasq.d/01-pihole.conf..." cp ${dnsmasq_pihole_01_snippet} ${dnsmasq_pihole_01_location} echo " done." sed -i "s/@INT@/$PIHOLE_INTERFACE/" ${dnsmasq_pihole_01_location} if [[ "${PIHOLE_DNS_1}" != "" ]]; then sed -i "s/@DNS1@/$PIHOLE_DNS_1/" ${dnsmasq_pihole_01_location} else sed -i '/^server=@DNS1@/d' ${dnsmasq_pihole_01_location} fi if [[ "${PIHOLE_DNS_2}" != "" ]]; then sed -i "s/@DNS2@/$PIHOLE_DNS_2/" ${dnsmasq_pihole_01_location} else sed -i '/^server=@DNS2@/d' ${dnsmasq_pihole_01_location} fi sed -i 's/^#conf-dir=\/etc\/dnsmasq.d$/conf-dir=\/etc\/dnsmasq.d/' ${dnsmasq_conf} if [[ "${QUERY_LOGGING}" == false ]] ; then #Disable Logging sed -i 's/^log-queries/#log-queries/' ${dnsmasq_pihole_01_location} else #Enable Logging sed -i 's/^#log-queries/log-queries/' ${dnsmasq_pihole_01_location} fi } remove_legacy_scripts() { #Tidy up /usr/local/bin directory if installing over previous install. oldFiles=( gravity chronometer whitelist blacklist piholeLogFlush updateDashboard uninstall setupLCD piholeDebug) for i in "${oldFiles[@]}"; do if [ -f "/usr/local/bin/$i.sh" ]; then rm /usr/local/bin/"$i".sh fi done } clean_existing() { # Clean an exiting installation to prepare for upgrade/reinstall # ${1} Directory to clean; ${2} Array of files to remove local clean_directory="${1}" local old_files=${2} for script in "${old_files[@]}"; do rm -f "${clean_directory}${script}.sh" done } installScripts() { # Install the scripts from repository to their various locations readonly install_dir="/opt/pihole/" echo ":::" echo -n "::: Installing scripts from ${PI_HOLE_LOCAL_REPO}..." # Clear out script files from Pi-hole scripts directory. clean_existing "${install_dir}" "${PI_HOLE_FILES}" # Install files from local core repository if is_repo "${PI_HOLE_LOCAL_REPO}"; then cd "${PI_HOLE_LOCAL_REPO}" install -o "${USER}" -Dm755 -t /opt/pihole/ gravity.sh install -o "${USER}" -Dm755 -t /opt/pihole/ ./advanced/Scripts/*.sh install -o "${USER}" -Dm755 -t /opt/pihole/ ./automated\ install/uninstall.sh install -o "${USER}" -Dm755 -t /usr/local/bin/ pihole install -Dm644 ./advanced/bash-completion/pihole /etc/bash_completion.d/pihole echo " done." else echo " *** ERROR: Local repo ${core_repo} not found, exiting." exit 1 fi } installConfigs() { # Install the configs from /etc/.pihole to their various locations echo ":::" echo "::: Installing configs..." version_check_dnsmasq if [ ! -d "/etc/lighttpd" ]; then mkdir /etc/lighttpd chown "${USER}":root /etc/lighttpd elif [ -f "/etc/lighttpd/lighttpd.conf" ]; then mv /etc/lighttpd/lighttpd.conf /etc/lighttpd/lighttpd.conf.orig fi cp /etc/.pihole/advanced/${LIGHTTPD_CFG} /etc/lighttpd/lighttpd.conf mkdir -p /var/run/lighttpd chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/run/lighttpd mkdir -p /var/cache/lighttpd/compress chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/compress mkdir -p /var/cache/lighttpd/uploads chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/uploads } stop_service() { # Stop service passed in as argument. # Can softfail, as process may not be installed when this is called echo ":::" echo -n "::: Stopping ${1} service..." if [ -x "$(command -v systemctl)" ]; then systemctl stop "${1}" &> /dev/null || true else service "${1}" stop &> /dev/null || true fi echo " done." } start_service() { # Start/Restart service passed in as argument # This should not fail, it's an error if it does echo ":::" echo -n "::: Starting ${1} service..." if [ -x "$(command -v systemctl)" ]; then systemctl restart "${1}" &> /dev/null else service "${1}" restart &> /dev/null fi echo " done." } enable_service() { # Enable service so that it will start with next reboot echo ":::" echo -n "::: Enabling ${1} service to start on reboot..." if [ -x "$(command -v systemctl)" ]; then systemctl enable "${1}" &> /dev/null else update-rc.d "${1}" defaults &> /dev/null fi echo " done." } update_pacakge_cache() { #Running apt-get update/upgrade with minimal output can cause some issues with #requiring user input (e.g password for phpmyadmin see #218) #Check to see if apt-get update has already been run today #it needs to have been run at least once on new installs! timestamp=$(stat -c %Y ${PKG_CACHE}) timestampAsDate=$(date -d @"${timestamp}" "+%b %e") today=$(date "+%b %e") if [ ! "${today}" == "${timestampAsDate}" ]; then #update package lists echo ":::" echo -n "::: ${PKG_MANAGER} update has not been run today. Running now..." ${UPDATE_PKG_CACHE} &> /dev/null echo " done!" fi } notify_package_updates_available() { # Let user know if they have outdated packages on their system and # advise them to run a package update at soonest possible. echo ":::" echo -n "::: Checking ${PKG_MANAGER} for upgraded packages...." updatesToInstall=$(eval "${PKG_COUNT}") echo " done!" echo ":::" if [[ ${updatesToInstall} -eq "0" ]]; then echo "::: Your system is up to date! Continuing with Pi-hole installation..." else echo "::: There are ${updatesToInstall} updates available for your system!" echo "::: We recommend you run '${PKG_UPDATE}' after installing Pi-Hole! " echo ":::" fi } install_dependent_packages() { # Install packages passed in via argument array # No spinner - conflicts with set -e declare -a argArray1=("${!1}") if command -v debconf-apt-progress &> /dev/null; then debconf-apt-progress -- ${PKG_INSTALL} "${argArray1[@]}" else for i in "${argArray1[@]}"; do echo -n "::: Checking for $i..." package_check_install "${i}" &> /dev/null echo " installed!" done fi } CreateLogFile() { # Create logfiles if necessary echo ":::" echo -n "::: Creating log file and changing owner to dnsmasq..." if [ ! -f /var/log/pihole.log ]; then touch /var/log/pihole.log chmod 644 /var/log/pihole.log chown "${DNSMASQ_USER}":root /var/log/pihole.log echo " done!" else echo " already exists!" fi } installPiholeWeb() { # Install the web interface echo ":::" echo "::: Installing pihole custom index page..." if [ -d "/var/www/html/pihole" ]; then if [ -f "/var/www/html/pihole/index.html" ]; then echo "::: Existing index.html detected, not overwriting" else echo -n "::: index.html missing, replacing... " cp /etc/.pihole/advanced/index.html /var/www/html/pihole/ echo " done!" fi if [ -f "/var/www/html/pihole/index.js" ]; then echo "::: Existing index.js detected, not overwriting" else echo -n "::: index.js missing, replacing... " cp /etc/.pihole/advanced/index.js /var/www/html/pihole/ echo " done!" fi else mkdir /var/www/html/pihole if [ -f /var/www/html/index.lighttpd.html ]; then mv /var/www/html/index.lighttpd.html /var/www/html/index.lighttpd.orig else printf "\n:::\tNo default index.lighttpd.html file found... not backing up" fi cp /etc/.pihole/advanced/index.* /var/www/html/pihole/. echo " done!" fi # Install Sudoer file echo ":::" echo -n "::: Installing sudoer file..." mkdir -p /etc/sudoers.d/ cp /etc/.pihole/advanced/pihole.sudo /etc/sudoers.d/pihole chmod 0440 /etc/sudoers.d/pihole echo " done!" } installCron() { # Install the cron job echo ":::" echo -n "::: Installing latest Cron script..." cp /etc/.pihole/advanced/pihole.cron /etc/cron.d/pihole echo " done!" } runGravity() { # Run gravity.sh to build blacklists echo ":::" echo "::: Preparing to run gravity.sh to refresh hosts..." if ls /etc/pihole/list* 1> /dev/null 2>&1; then echo "::: Cleaning up previous install (preserving whitelist/blacklist)" rm /etc/pihole/list.* fi echo "::: Running gravity.sh" /opt/pihole/gravity.sh } create_pihole_user() { # Check if user pihole exists and create if not echo "::: Checking if user 'pihole' exists..." id -u pihole &> /dev/null && echo "::: User 'pihole' already exists" || (echo "::: User 'pihole' doesn't exist. Creating..." && useradd -r -s /usr/sbin/nologin pihole) } configureFirewall() { # Allow HTTP and DNS traffic if [ -x "$(command -v firewall-cmd)" ]; then firewall-cmd --state &> /dev/null && ( echo "::: Configuring firewalld for httpd and dnsmasq.." && firewall-cmd --permanent --add-port=80/tcp && firewall-cmd --permanent --add-port=53/tcp \ && firewall-cmd --permanent --add-port=53/udp && firewall-cmd --reload) || echo "::: FirewallD not enabled" elif [ -x "$(command -v iptables)" ]; then echo "::: Configuring iptables for httpd and dnsmasq.." iptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT iptables -A INPUT -p tcp -m tcp --dport 53 -j ACCEPT iptables -A INPUT -p udp -m udp --dport 53 -j ACCEPT else echo "::: No firewall detected.. skipping firewall configuration." fi } finalExports() { # Update variables in setupVars.conf file if [ -e "${setupVars}" ]; then sed -i.update.bak '/PIHOLE_INTERFACE/d;/IPV4_ADDRESS/d;/IPV6_ADDRESS/d;/PIHOLE_DNS_1/d;/PIHOLE_DNS_2/d;/QUERY_LOGGING/d;' "${setupVars}" fi { echo "PIHOLE_INTERFACE=${PIHOLE_INTERFACE}" echo "IPV4_ADDRESS=${IPV4_ADDRESS}" echo "IPV6_ADDRESS=${IPV6_ADDRESS}" echo "PIHOLE_DNS_1=${PIHOLE_DNS_1}" echo "PIHOLE_DNS_2=${PIHOLE_DNS_2}" echo "QUERY_LOGGING=${QUERY_LOGGING}" }>> "${setupVars}" } installPihole() { # Install base files and web interface create_pihole_user if [ ! -d "/var/www/html" ]; then mkdir -p /var/www/html fi chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/www/html chmod 775 /var/www/html usermod -a -G ${LIGHTTPD_GROUP} pihole if [ -x "$(command -v lighty-enable-mod)" ]; then lighty-enable-mod fastcgi fastcgi-php > /dev/null || true else printf "\n:::\tWarning: 'lighty-enable-mod' utility not found. Please ensure fastcgi is enabled if you experience issues.\n" fi installScripts installConfigs CreateLogFile configureSelinux installPiholeWeb installCron configureFirewall finalExports runGravity } accountForRefactor() { # At some point in the future this list can be pruned, for now we'll need it to ensure updates don't break. # Refactoring of install script has changed the name of a couple of variables. Sort them out here. sed -i 's/piholeInterface/PIHOLE_INTERFACE/g' ${setupVars} sed -i 's/IPv4_address/IPV4_ADDRESS/g' ${setupVars} sed -i 's/IPv4addr/IPV4_ADDRESS/g' ${setupVars} sed -i 's/IPv6_address/IPV6_ADDRESS/g' ${setupVars} sed -i 's/piholeIPv6/IPV6_ADDRESS/g' ${setupVars} sed -i 's/piholeDNS1/PIHOLE_DNS_1/g' ${setupVars} sed -i 's/piholeDNS2/PIHOLE_DNS_2/g' ${setupVars} } updatePihole() { accountForRefactor # Source ${setupVars} for use in the rest of the functions. . ${setupVars} # Install base files and web interface installScripts installConfigs CreateLogFile configureSelinux installPiholeWeb installCron configureFirewall finalExports #re-export setupVars.conf to account for any new vars added in new versions runGravity } configureSelinux() { if [ -x "$(command -v getenforce)" ]; then printf "\n::: SELinux Detected\n" printf ":::\tChecking for SELinux policy development packages..." package_check_install "selinux-policy-devel" > /dev/null echo " installed!" printf ":::\tEnabling httpd server side includes (SSI).. " setsebool -P httpd_ssi_exec on &> /dev/null && echo "Success" || echo "SELinux not enabled" printf "\n:::\tCompiling Pi-Hole SELinux policy..\n" if ! [ -x "$(command -v systemctl)" ]; then sed -i.bak '/systemd/d' /etc/.pihole/advanced/selinux/pihole.te fi checkmodule -M -m -o /etc/pihole/pihole.mod /etc/.pihole/advanced/selinux/pihole.te semodule_package -o /etc/pihole/pihole.pp -m /etc/pihole/pihole.mod semodule -i /etc/pihole/pihole.pp rm -f /etc/pihole/pihole.mod semodule -l | grep pihole &> /dev/null && echo "::: Installed Pi-Hole SELinux policy" || echo "::: Warning: Pi-Hole SELinux policy did not install." fi } displayFinalMessage() { if (( ${#1} > 0 )) ; then # Final completion message to user whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: IPv4: ${IPV4_ADDRESS%/*} IPv6: ${IPV6_ADDRESS} If you set a new IP address, you should restart the Pi. The install log is in /etc/pihole. View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin The currently set password is ${1}" ${r} ${c} else whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: IPv4: ${IPV4_ADDRESS%/*} IPv6: ${IPV6_ADDRESS} If you set a new IP address, you should restart the Pi. The install log is in /etc/pihole. View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin" ${r} ${c} fi } update_dialogs() { # reconfigure if [ "${reconfigure}" = true ]; then opt1a="Repair" opt1b="This will retain existing settings" strAdd="You will remain on the same version" else opt1a="Update" opt1b="This will retain existing settings." strAdd="You will be updated to the latest version." fi opt2a="Reconfigure" opt2b="This will allow you to enter new settings" UpdateCmd=$(whiptail --title "Existing Install Detected!" --menu "\n\nWe have detected an existing install.\n\nPlease choose from the following options: \n($strAdd)" ${r} ${c} 2 \ "${opt1a}" "${opt1b}" \ "${opt2a}" "${opt2b}" 3>&2 2>&1 1>&3) if [[ $? = 0 ]];then case ${UpdateCmd} in ${opt1a}) echo "::: ${opt1a} option selected." useUpdateVars=true ;; ${opt2a}) echo "::: ${opt2a} option selected" useUpdateVars=false ;; esac else echo "::: Cancel selected. Exiting..." exit 1 fi } main() { # Check arguments for the undocumented flags for var in "$@"; do case "$var" in "--reconfigure" ) reconfigure=true;; "--i_do_not_follow_recommendations" ) skipSpaceCheck=false;; "--unattended" ) runUnattended=true;; esac done if [[ -f ${setupVars} ]]; then if [[ "${runUnattended}" == true ]]; then echo "::: --unattended passed to install script, no whiptail dialogs will be displayed" useUpdateVars=true else update_dialogs fi fi # Start the installer # Verify there is enough disk space for the install if [[ "${skipSpaceCheck}" == true ]]; then echo "::: --i_do_not_follow_recommendations passed to script, skipping free disk space verification!" else verifyFreeDiskSpace fi # Update package cache update_pacakge_cache # Notify user of package availability notify_package_updates_available # Install packages used by this installation script install_dependent_packages INSTALLER_DEPS[@] if [[ "${reconfigure}" == true ]]; then echo "::: --reconfigure passed to install script. Not downloading/updating local repos" else # Get Git files for Core and Admin getGitFiles ${PI_HOLE_LOCAL_REPO} ${piholeGitUrl} getGitFiles ${webInterfaceDir} ${webInterfaceGitUrl} fi if [[ ${useUpdateVars} == false ]]; then # Display welcome dialogs welcomeDialogs # Create directory for Pi-hole storage mkdir -p /etc/pihole/ # Remove legacy scripts from previous storage location remove_legacy_scripts # Stop resolver and webserver while installing proceses stop_service dnsmasq stop_service lighttpd # Determine available interfaces get_available_interfaces # Find interfaces and let the user choose one chooseInterface # Let the user decide if they want to block ads over IPv4 and/or IPv6 use4andor6 # Decide what upstream DNS Servers to use setDNS # Let the user decide if they want query logging enabled... setLogging # Install packages used by the Pi-hole install_dependent_packages PIHOLE_DEPS[@] # Install and log everything to a file installPihole | tee ${tmpLog} else # update packages used by the Pi-hole install_dependent_packages PIHOLE_DEPS[@] updatePihole | tee ${tmpLog} fi # Move the log file into /etc/pihole for storage mv ${tmpLog} ${instalLogLoc} # Add password to web UI if there is none pw="" if [[ $(grep 'WEBPASSWORD' -c /etc/pihole/setupVars.conf) == 0 ]] ; then pw=$(tr -dc _A-Z-a-z-0-9 < /dev/urandom | head -c 8) pihole -a -p ${pw} fi if [[ "${useUpdateVars}" == false ]]; then displayFinalMessage ${pw} fi echo "::: Restarting services..." # Start services start_service dnsmasq enable_service dnsmasq start_service lighttpd enable_service lighttpd echo "::: done." echo ":::" if [[ "${useUpdateVars}" == false ]]; then echo "::: Installation Complete! Configure your devices to use the Pi-hole as their DNS server using:" echo "::: ${IPV4_ADDRESS%/*}" echo "::: ${IPV6_ADDRESS}" echo ":::" echo "::: If you set a new IP address, you should restart the Pi." echo "::: View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin" else echo "::: Update complete!" fi if (( ${#pw} > 0 )) ; then echo ":::" echo "::: Note: As security measure a password has been installed for your web interface" echo "::: The currently set password is" echo "::: ${pw}" echo ":::" echo "::: You can always change it using" echo "::: pihole -a -p new_password" fi echo ":::" echo "::: The install log is located at: /etc/pihole/install.log" } if [[ "${PH_TEST}" != true ]] ; then main "$@" fi \ No newline at end of file +#!/usr/bin/env bash +# Pi-hole: A black hole for Internet advertisements +# (c) 2015, 2016 by Jacob Salmela +# Network-wide ad blocking via your Raspberry Pi +# http://pi-hole.net +# Installs Pi-hole +# +# Pi-hole is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. + +# pi-hole.net/donate +# +# Install with this command (from your Pi): +# +# curl -L install.pi-hole.net | bash + +set -e +######## VARIABLES ######### +tmpLog=/tmp/pihole-install.log +instalLogLoc=/etc/pihole/install.log +setupVars=/etc/pihole/setupVars.conf + +webInterfaceGitUrl="https://github.com/pi-hole/AdminLTE.git" +webInterfaceDir="/var/www/html/admin" +piholeGitUrl="https://github.com/pi-hole/pi-hole.git" +PI_HOLE_LOCAL_REPO="/etc/.pihole" +PI_HOLE_FILES=(chronometer list piholeDebug piholeLogFlush setupLCD update version) +useUpdateVars=false + +IPV4_ADDRESS="" +IPV6_ADDRESS="" +QUERY_LOGGING=true + +# Find the rows and columns will default to 80x24 is it can not be detected +screen_size=$(stty size 2>/dev/null || echo 24 80) +rows=$(echo $screen_size | awk '{print $1}') +columns=$(echo $screen_size | awk '{print $2}') + +# Divide by two so the dialogs take up half of the screen, which looks nice. +r=$(( rows / 2 )) +c=$(( columns / 2 )) +# Unless the screen is tiny +r=$(( r < 20 ? 20 : r )) +c=$(( c < 70 ? 70 : c )) + +######## Undocumented Flags. Shhh ######## +skipSpaceCheck=false +reconfigure=false +runUnattended=false + +######## FIRST CHECK ######## +# Must be root to install +echo ":::" +if [[ ${EUID} -eq 0 ]]; then + echo "::: You are root." +else + echo "::: Script called with non-root privileges. The Pi-hole installs server packages and configures" + echo "::: system networking, it requires elevated rights. Please check the contents of the script for" + echo "::: any concerns with this requirement. Please be sure to download this script from a trusted source." + echo ":::" + echo "::: Detecting the presence of the sudo utility for continuation of this install..." + + if [ -x "$(command -v sudo)" ]; then + echo "::: Utility sudo located." + exec curl -sSL https://install.pi-hole.net | sudo bash "$@" + exit $? + else + echo "::: sudo is needed for the Web interface to run pihole commands. Please run this script as root and it will be automatically installed." + exit 1 + fi +fi + +# Compatibility + +if [[ $(command -v apt-get) ]]; then + #Debian Family + ############################################# + PKG_MANAGER="apt-get" + PKG_CACHE="/var/lib/apt/lists/" + UPDATE_PKG_CACHE="${PKG_MANAGER} update" + PKG_UPDATE="${PKG_MANAGER} upgrade" + PKG_INSTALL="${PKG_MANAGER} --yes --fix-missing install" + # grep -c will return 1 retVal on 0 matches, block this throwing the set -e with an OR TRUE + PKG_COUNT="${PKG_MANAGER} -s -o Debug::NoLocking=true upgrade | grep -c ^Inst || true" + # ######################################### + # fixes for dependancy differences + # Debian 7 doesn't have iproute2 use iproute + ${PKG_MANAGER} install --dry-run iproute2 > /dev/null 2>&1 && IPROUTE_PKG="iproute2" || IPROUTE_PKG="iproute" + # Prefer the php metapackage if it's there, fall back on the php5 pacakges + ${PKG_MANAGER} install --dry-run php > /dev/null 2>&1 && phpVer="php" || phpVer="php5" + # ######################################### + INSTALLER_DEPS=(apt-utils debconf dhcpcd5 git whiptail) + PIHOLE_DEPS=(bc cron curl dnsmasq dnsutils ${IPROUTE_PKG} iputils-ping lighttpd lsof netcat ${phpVer}-common ${phpVer}-cgi sudo unzip wget) + LIGHTTPD_USER="www-data" + LIGHTTPD_GROUP="www-data" + LIGHTTPD_CFG="lighttpd.conf.debian" + DNSMASQ_USER="dnsmasq" + + package_check_install() { + dpkg-query -W -f='${Status}' "${1}" 2>/dev/null | grep -c "ok installed" || ${PKG_INSTALL} "${1}" + } +elif [ $(command -v rpm) ]; then + # Fedora Family + if [ $(command -v dnf) ]; then + PKG_MANAGER="dnf" + else + PKG_MANAGER="yum" + fi + PKG_CACHE="/var/cache/${PKG_MANAGER}" + UPDATE_PKG_CACHE="${PKG_MANAGER} check-update" + PKG_UPDATE="${PKG_MANAGER} update -y" + PKG_INSTALL="${PKG_MANAGER} install -y" + PKG_COUNT="${PKG_MANAGER} check-update | egrep '(.i686|.x86|.noarch|.arm|.src)' | wc -l" + INSTALLER_DEPS=(git iproute net-tools newt procps-ng) + PIHOLE_DEPS=(bc bind-utils cronie curl dnsmasq epel-release findutils lighttpd lighttpd-fastcgi nmap-ncat php php-common php-cli sudo unzip wget) + + if grep -q 'Fedora' /etc/redhat-release; then + remove_deps=(epel-release); + PIHOLE_DEPS=( ${PIHOLE_DEPS[@]/$remove_deps} ); + fi + LIGHTTPD_USER="lighttpd" + LIGHTTPD_GROUP="lighttpd" + LIGHTTPD_CFG="lighttpd.conf.fedora" + DNSMASQ_USER="nobody" + + package_check_install() { + rpm -qa | grep ^"${1}"- > /dev/null || ${PKG_INSTALL} "${1}" + } +else + echo "OS distribution not supported" + exit +fi + +####### FUNCTIONS ########## +is_repo() { + # Use git to check if directory is currently under VCS, return the value + local directory="${1}" + git -C "${directory}" status --short &> /dev/null + return +} + +make_repo() { + local directory="${1}" + local remoteRepo="${2}" + # Remove the non-repod interface and clone the interface + echo -n "::: Cloning $remoteRepo into $directory..." + rm -rf "${directory}" + git clone -q --depth 1 "${remoteRepo}" "${directory}" &> /dev/null + echo " done!" +} + +update_repo() { + local directory="${1}" + # Pull the latest commits + echo -n "::: Updating repo in $1..." + cd "${directory}" || exit 1 + git stash -q &> /dev/null + git pull -q &> /dev/null + echo " done!" +} + +getGitFiles() { + # Setup git repos for directory and repository passed + # as arguments 1 and 2 + local directory="${1}" + local remoteRepo="${2}" + echo ":::" + echo "::: Checking for existing repository..." + if is_repo "${directory}"; then + update_repo "${directory}" + else + make_repo "${directory}" "${remoteRepo}" + fi +} + +find_IPv4_information() { + # Find IP used to route to outside world + IPv4dev=$(ip route get 8.8.8.8 | awk '{for(i=1;i<=NF;i++)if($i~/dev/)print $(i+1)}') + IPV4_ADDRESS=$(ip -o -f inet addr show dev "$IPv4dev" | awk '{print $4}' | awk 'END {print}') + IPv4gw=$(ip route get 8.8.8.8 | awk '{print $3}') +} + +get_available_interfaces() { + # Get available interfaces. Consider only getting UP interfaces in the future, and leaving DOWN interfaces out of list. + availableInterfaces=$(ip -o link | awk '{print $2}' | grep -v "lo" | cut -d':' -f1 | cut -d'@' -f1) +} + +welcomeDialogs() { + # Display the welcome dialog + whiptail --msgbox --backtitle "Welcome" --title "Pi-hole automated installer" "\n\nThis installer will transform your device into a network-wide ad blocker!" ${r} ${c} + + # Support for a part-time dev + whiptail --msgbox --backtitle "Plea" --title "Free and open source" "\n\nThe Pi-hole is free, but powered by your donations: http://pi-hole.net/donate" ${r} ${c} + + # Explain the need for a static address + whiptail --msgbox --backtitle "Initiating network interface" --title "Static IP Needed" "\n\nThe Pi-hole is a SERVER so it needs a STATIC IP ADDRESS to function properly. + +In the next section, you can choose to use your current network settings (DHCP) or to manually edit them." ${r} ${c} +} + +verifyFreeDiskSpace() { + + # 50MB is the minimum space needed (45MB install (includes web admin bootstrap/jquery libraries etc) + 5MB one day of logs.) + # - Fourdee: Local ensures the variable is only created, and accessible within this function/void. Generally considered a "good" coding practice for non-global variables. + echo "::: Verifying free disk space..." + local required_free_kilobytes=51200 + local existing_free_kilobytes=$(df -Pk | grep -m1 '\/$' | awk '{print $4}') + + # - Unknown free disk space , not a integer + if ! [[ "${existing_free_kilobytes}" =~ ^([0-9])+$ ]]; then + echo "::: Unknown free disk space!" + echo "::: We were unable to determine available free disk space on this system." + echo "::: You may override this check and force the installation, however, it is not recommended" + echo "::: To do so, pass the argument '--i_do_not_follow_recommendations' to the install script" + echo "::: eg. curl -L https://install.pi-hole.net | bash /dev/stdin --i_do_not_follow_recommendations" + exit 1 + # - Insufficient free disk space + elif [[ ${existing_free_kilobytes} -lt ${required_free_kilobytes} ]]; then + echo "::: Insufficient Disk Space!" + echo "::: Your system appears to be low on disk space. pi-hole recommends a minimum of $required_free_kilobytes KiloBytes." + echo "::: You only have ${existing_free_kilobytes} KiloBytes free." + echo "::: If this is a new install you may need to expand your disk." + echo "::: Try running 'sudo raspi-config', and choose the 'expand file system option'" + echo "::: After rebooting, run this installation again. (curl -L https://install.pi-hole.net | bash)" + + echo "Insufficient free space, exiting..." + exit 1 + + fi + +} + + +chooseInterface() { + # Turn the available interfaces into an array so it can be used with a whiptail dialog + local interfacesArray=() + # Number of available interfaces + local interfaceCount + # Whiptail variable storage + local chooseInterfaceCmd + # Temporary Whiptail options storage + local chooseInterfaceOptions + # Loop sentinel variable + local firstLoop=1 + + while read -r line; do + mode="OFF" + if [[ ${firstLoop} -eq 1 ]]; then + firstLoop=0 + mode="ON" + fi + interfacesArray+=("${line}" "available" "${mode}") + done <<< "${availableInterfaces}" + + # Find out how many interfaces are available to choose from + interfaceCount=$(echo "${availableInterfaces}" | wc -l) + chooseInterfaceCmd=(whiptail --separate-output --radiolist "Choose An Interface (press space to select)" ${r} ${c} ${interfaceCount}) + chooseInterfaceOptions=$("${chooseInterfaceCmd[@]}" "${interfacesArray[@]}" 2>&1 >/dev/tty) + if [[ $? = 0 ]]; then + for desiredInterface in ${chooseInterfaceOptions}; do + PIHOLE_INTERFACE=${desiredInterface} + echo "::: Using interface: $PIHOLE_INTERFACE" + done + else + echo "::: Cancel selected, exiting...." + exit 1 + fi +} + +useIPv6dialog() { + # Show the IPv6 address used for blocking + IPV6_ADDRESS=$(ip -6 route get 2001:4860:4860::8888 | awk -F " " '{ for(i=1;i<=NF;i++) if ($i == "src") print $(i+1) }') + whiptail --msgbox --backtitle "IPv6..." --title "IPv6 Supported" "$IPV6_ADDRESS will be used to block ads." ${r} ${c} +} + + +use4andor6() { + local useIPv4 + local useIPv6 + # Let use select IPv4 and/or IPv6 + cmd=(whiptail --separate-output --checklist "Select Protocols (press space to select)" ${r} ${c} 2) + options=(IPv4 "Block ads over IPv4" on + IPv6 "Block ads over IPv6" on) + choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty) + if [[ $? = 0 ]];then + for choice in ${choices} + do + case ${choice} in + IPv4 ) useIPv4=true;; + IPv6 ) useIPv6=true;; + esac + done + if [[ ${useIPv4} ]]; then + find_IPv4_information + getStaticIPv4Settings + setStaticIPv4 + fi + if [[ ${useIPv6} ]]; then + useIPv6dialog + fi + echo "::: IPv4 address: ${IPV4_ADDRESS}" + echo "::: IPv6 address: ${IPV6_ADDRESS}" + if [ ! ${useIPv4} ] && [ ! ${useIPv6} ]; then + echo "::: Cannot continue, neither IPv4 or IPv6 selected" + echo "::: Exiting" + exit 1 + fi + else + echo "::: Cancel selected. Exiting..." + exit 1 + fi +} + +getStaticIPv4Settings() { + # Ask if the user wants to use DHCP settings as their static IP + if (whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Do you want to use your current network settings as a static address? + IP address: ${IPV4_ADDRESS} + Gateway: ${IPv4gw}" ${r} ${c}); then + # If they choose yes, let the user know that the IP address will not be available via DHCP and may cause a conflict. + whiptail --msgbox --backtitle "IP information" --title "FYI: IP Conflict" "It is possible your router could still try to assign this IP to a device, which would cause a conflict. But in most cases the router is smart enough to not do that. +If you are worried, either manually set the address, or modify the DHCP reservation pool so it does not include the IP you want. +It is also possible to use a DHCP reservation, but if you are going to do that, you might as well set a static address." ${r} ${c} + # Nothing else to do since the variables are already set above + else + # Otherwise, we need to ask the user to input their desired settings. + # Start by getting the IPv4 address (pre-filling it with info gathered from DHCP) + # Start a loop to let the user enter their information with the chance to go back and edit it if necessary + until [[ ${ipSettingsCorrect} = True ]]; do + # Ask for the IPv4 address + IPV4_ADDRESS=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 address" --inputbox "Enter your desired IPv4 address" ${r} ${c} "${IPV4_ADDRESS}" 3>&1 1>&2 2>&3) + if [[ $? = 0 ]]; then + echo "::: Your static IPv4 address: ${IPV4_ADDRESS}" + # Ask for the gateway + IPv4gw=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 gateway (router)" --inputbox "Enter your desired IPv4 default gateway" ${r} ${c} "${IPv4gw}" 3>&1 1>&2 2>&3) + if [[ $? = 0 ]]; then + echo "::: Your static IPv4 gateway: ${IPv4gw}" + # Give the user a chance to review their settings before moving on + if (whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Are these settings correct? + IP address: ${IPV4_ADDRESS} + Gateway: ${IPv4gw}" ${r} ${c}); then + # After that's done, the loop ends and we move on + ipSettingsCorrect=True + else + # If the settings are wrong, the loop continues + ipSettingsCorrect=False + fi + else + # Cancelling gateway settings window + ipSettingsCorrect=False + echo "::: Cancel selected. Exiting..." + exit 1 + fi + else + # Cancelling IPv4 settings window + ipSettingsCorrect=False + echo "::: Cancel selected. Exiting..." + exit 1 + fi + done + # End the if statement for DHCP vs. static + fi +} + +setDHCPCD() { + # Append these lines to dhcpcd.conf to enable a static IP + echo "## interface ${PIHOLE_INTERFACE} + static ip_address=${IPV4_ADDRESS} + static routers=${IPv4gw} + static domain_name_servers=${IPv4gw}" | tee -a /etc/dhcpcd.conf >/dev/null +} + +setStaticIPv4() { + local IFCFG_FILE + local IPADDR + local CIDR + if [[ -f /etc/dhcpcd.conf ]]; then + # Debian Family + if grep -q "${IPV4_ADDRESS}" /etc/dhcpcd.conf; then + echo "::: Static IP already configured" + else + setDHCPCD + ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" + echo ":::" + echo "::: Setting IP to ${IPV4_ADDRESS}. You may need to restart after the install is complete." + echo ":::" + fi + elif [[ -f /etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE} ]];then + # Fedora Family + IFCFG_FILE=/etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE} + if grep -q "${IPV4_ADDRESS}" "${IFCFG_FILE}"; then + echo "::: Static IP already configured" + else + IPADDR=$(echo "${IPV4_ADDRESS}" | cut -f1 -d/) + CIDR=$(echo "${IPV4_ADDRESS}" | cut -f2 -d/) + # Backup existing interface configuration: + cp "${IFCFG_FILE}" "${IFCFG_FILE}".pihole.orig + # Build Interface configuration file: + { + echo "# Configured via Pi-Hole installer" + echo "DEVICE=$PIHOLE_INTERFACE" + echo "BOOTPROTO=none" + echo "ONBOOT=yes" + echo "IPADDR=$IPADDR" + echo "PREFIX=$CIDR" + echo "GATEWAY=$IPv4gw" + echo "DNS1=$PIHOLE_DNS_1" + echo "DNS2=$PIHOLE_DNS_2" + echo "USERCTL=no" + }> "${IFCFG_FILE}" + ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" + if [ -x "$(command -v nmcli)" ];then + # Tell NetworkManager to read our new sysconfig file + nmcli con load "${IFCFG_FILE}" > /dev/null + fi + echo ":::" + echo "::: Setting IP to ${IPV4_ADDRESS}. You may need to restart after the install is complete." + echo ":::" + fi + else + echo "::: Warning: Unable to locate configuration file to set static IPv4 address!" + exit 1 + fi +} + +valid_ip() { + local ip=${1} + local stat=1 + + if [[ ${ip} =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then + OIFS=$IFS + IFS='.' + ip=(${ip}) + IFS=${OIFS} + [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \ + && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]] + stat=$? + fi + return ${stat} +} + +setDNS() { + DNSChooseCmd=(whiptail --separate-output --radiolist "Select Upstream DNS Provider. To use your own, select Custom." ${r} ${c} 6) + DNSChooseOptions=(Google "" on + OpenDNS "" off + Level3 "" off + Norton "" off + Comodo "" off + Custom "" off) + DNSchoices=$("${DNSChooseCmd[@]}" "${DNSChooseOptions[@]}" 2>&1 >/dev/tty) + if [[ $? = 0 ]];then + case ${DNSchoices} in + Google) + echo "::: Using Google DNS servers." + PIHOLE_DNS_1="8.8.8.8" + PIHOLE_DNS_2="8.8.4.4" + ;; + OpenDNS) + echo "::: Using OpenDNS servers." + PIHOLE_DNS_1="208.67.222.222" + PIHOLE_DNS_2="208.67.220.220" + ;; + Level3) + echo "::: Using Level3 servers." + PIHOLE_DNS_1="4.2.2.1" + PIHOLE_DNS_2="4.2.2.2" + ;; + Norton) + echo "::: Using Norton ConnectSafe servers." + PIHOLE_DNS_1="199.85.126.10" + PIHOLE_DNS_2="199.85.127.10" + ;; + Comodo) + echo "::: Using Comodo Secure servers." + PIHOLE_DNS_1="8.26.56.26" + PIHOLE_DNS_2="8.20.247.20" + ;; + Custom) + until [[ ${DNSSettingsCorrect} = True ]]; do + strInvalid="Invalid" + if [ ! ${PIHOLE_DNS_1} ]; then + if [ ! ${PIHOLE_DNS_2} ]; then + prePopulate="" + else + prePopulate=", ${PIHOLE_DNS_2}" + fi + elif [ ${PIHOLE_DNS_1} ] && [ ! ${PIHOLE_DNS_2} ]; then + prePopulate="${PIHOLE_DNS_1}" + elif [ ${PIHOLE_DNS_1} ] && [ ${PIHOLE_DNS_2} ]; then + prePopulate="${PIHOLE_DNS_1}, ${PIHOLE_DNS_2}" + fi + + piholeDNS=$(whiptail --backtitle "Specify Upstream DNS Provider(s)" --inputbox "Enter your desired upstream DNS provider(s), seperated by a comma.\n\nFor example '8.8.8.8, 8.8.4.4'" ${r} ${c} "${prePopulate}" 3>&1 1>&2 2>&3) + + if [[ $? = 0 ]]; then + PIHOLE_DNS_1=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$1}') + PIHOLE_DNS_2=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$2}') + if ! valid_ip "${PIHOLE_DNS_1}" || [ ! "${PIHOLE_DNS_1}" ]; then + PIHOLE_DNS_1=${strInvalid} + fi + if ! valid_ip "${PIHOLE_DNS_2}" && [ "${PIHOLE_DNS_2}" ]; then + PIHOLE_DNS_2=${strInvalid} + fi + else + echo "::: Cancel selected, exiting...." + exit 1 + fi + if [[ ${PIHOLE_DNS_1} == "${strInvalid}" ]] || [[ ${PIHOLE_DNS_2} == "${strInvalid}" ]]; then + whiptail --msgbox --backtitle "Invalid IP" --title "Invalid IP" "One or both entered IP addresses were invalid. Please try again.\n\n DNS Server 1: $PIHOLE_DNS_1\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c} + if [[ ${PIHOLE_DNS_1} == "${strInvalid}" ]]; then + PIHOLE_DNS_1="" + fi + if [[ ${PIHOLE_DNS_2} == "${strInvalid}" ]]; then + PIHOLE_DNS_2="" + fi + DNSSettingsCorrect=False + else + if (whiptail --backtitle "Specify Upstream DNS Provider(s)" --title "Upstream DNS Provider(s)" --yesno "Are these settings correct?\n DNS Server 1: $PIHOLE_DNS_1\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c}); then + DNSSettingsCorrect=True + else + # If the settings are wrong, the loop continues + DNSSettingsCorrect=False + fi + fi + done + ;; + esac + else + echo "::: Cancel selected. Exiting..." + exit 1 + fi +} + +setLogging() { + local LogToggleCommand + local LogChooseOptions + local LogChoices + + LogToggleCommand=(whiptail --separate-output --radiolist "Do you want to log queries?\n (Disabling will render graphs on the Admin page useless):" ${r} ${c} 6) + LogChooseOptions=("On (Reccomended)" "" on + Off "" off) + LogChoices=$("${LogToggleCommand[@]}" "${LogChooseOptions[@]}" 2>&1 >/dev/tty) || (echo "::: Cancel selected. Exiting..." && exit 1) + case ${LogChoices} in + "On (Recommended)") + echo "::: Logging On." + QUERY_LOGGING=true + ;; + Off) + echo "::: Logging Off." + QUERY_LOGGING=false + ;; + esac +} + + +version_check_dnsmasq() { + # Check if /etc/dnsmasq.conf is from pihole. If so replace with an original and install new in .d directory + local dnsmasq_conf="/etc/dnsmasq.conf" + local dnsmasq_conf_orig="/etc/dnsmasq.conf.orig" + local dnsmasq_pihole_id_string="addn-hosts=/etc/pihole/gravity.list" + local dnsmasq_original_config="/etc/.pihole/advanced/dnsmasq.conf.original" + local dnsmasq_pihole_01_snippet="/etc/.pihole/advanced/01-pihole.conf" + local dnsmasq_pihole_01_location="/etc/dnsmasq.d/01-pihole.conf" + + if [ -f ${dnsmasq_conf} ]; then + echo -n "::: Existing dnsmasq.conf found..." + if grep -q ${dnsmasq_pihole_id_string} ${dnsmasq_conf}; then + echo " it is from a previous pi-hole install." + echo -n "::: Backing up dnsmasq.conf to dnsmasq.conf.orig..." + mv -f ${dnsmasq_conf} ${dnsmasq_conf_orig} + echo " done." + echo -n "::: Restoring default dnsmasq.conf..." + cp ${dnsmasq_original_config} ${dnsmasq_conf} + echo " done." + else + echo " it is not a pi-hole file, leaving alone!" + fi + else + echo -n "::: No dnsmasq.conf found.. restoring default dnsmasq.conf..." + cp ${dnsmasq_original_config} ${dnsmasq_conf} + echo " done." + fi + + echo -n "::: Copying 01-pihole.conf to /etc/dnsmasq.d/01-pihole.conf..." + cp ${dnsmasq_pihole_01_snippet} ${dnsmasq_pihole_01_location} + echo " done." + sed -i "s/@INT@/$PIHOLE_INTERFACE/" ${dnsmasq_pihole_01_location} + if [[ "${PIHOLE_DNS_1}" != "" ]]; then + sed -i "s/@DNS1@/$PIHOLE_DNS_1/" ${dnsmasq_pihole_01_location} + else + sed -i '/^server=@DNS1@/d' ${dnsmasq_pihole_01_location} + fi + if [[ "${PIHOLE_DNS_2}" != "" ]]; then + sed -i "s/@DNS2@/$PIHOLE_DNS_2/" ${dnsmasq_pihole_01_location} + else + sed -i '/^server=@DNS2@/d' ${dnsmasq_pihole_01_location} + fi + + sed -i 's/^#conf-dir=\/etc\/dnsmasq.d$/conf-dir=\/etc\/dnsmasq.d/' ${dnsmasq_conf} + + if [[ "${QUERY_LOGGING}" == false ]] ; then + #Disable Logging + sed -i 's/^log-queries/#log-queries/' ${dnsmasq_pihole_01_location} + else + #Enable Logging + sed -i 's/^#log-queries/log-queries/' ${dnsmasq_pihole_01_location} + fi +} + +remove_legacy_scripts() { + #Tidy up /usr/local/bin directory if installing over previous install. + oldFiles=( gravity chronometer whitelist blacklist piholeLogFlush updateDashboard uninstall setupLCD piholeDebug) + for i in "${oldFiles[@]}"; do + if [ -f "/usr/local/bin/$i.sh" ]; then + rm /usr/local/bin/"$i".sh + fi + done +} + +clean_existing() { + # Clean an exiting installation to prepare for upgrade/reinstall + # ${1} Directory to clean; ${2} Array of files to remove + local clean_directory="${1}" + local old_files=${2} + + for script in "${old_files[@]}"; do + rm -f "${clean_directory}${script}.sh" + done + +} + +installScripts() { + # Install the scripts from repository to their various locations + readonly install_dir="/opt/pihole/" + + echo ":::" + echo -n "::: Installing scripts from ${PI_HOLE_LOCAL_REPO}..." + + # Clear out script files from Pi-hole scripts directory. + clean_existing "${install_dir}" "${PI_HOLE_FILES}" + + # Install files from local core repository + if is_repo "${PI_HOLE_LOCAL_REPO}"; then + cd "${PI_HOLE_LOCAL_REPO}" + install -o "${USER}" -Dm755 -t /opt/pihole/ gravity.sh + install -o "${USER}" -Dm755 -t /opt/pihole/ ./advanced/Scripts/*.sh + install -o "${USER}" -Dm755 -t /opt/pihole/ ./automated\ install/uninstall.sh + install -o "${USER}" -Dm755 -t /usr/local/bin/ pihole + install -Dm644 ./advanced/bash-completion/pihole /etc/bash_completion.d/pihole + echo " done." + else + echo " *** ERROR: Local repo ${core_repo} not found, exiting." + exit 1 + fi +} + +installConfigs() { + # Install the configs from /etc/.pihole to their various locations + echo ":::" + echo "::: Installing configs..." + version_check_dnsmasq + if [ ! -d "/etc/lighttpd" ]; then + mkdir /etc/lighttpd + chown "${USER}":root /etc/lighttpd + elif [ -f "/etc/lighttpd/lighttpd.conf" ]; then + mv /etc/lighttpd/lighttpd.conf /etc/lighttpd/lighttpd.conf.orig + fi + cp /etc/.pihole/advanced/${LIGHTTPD_CFG} /etc/lighttpd/lighttpd.conf + mkdir -p /var/run/lighttpd + chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/run/lighttpd + mkdir -p /var/cache/lighttpd/compress + chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/compress + mkdir -p /var/cache/lighttpd/uploads + chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/uploads +} + +stop_service() { + # Stop service passed in as argument. + # Can softfail, as process may not be installed when this is called + echo ":::" + echo -n "::: Stopping ${1} service..." + if [ -x "$(command -v systemctl)" ]; then + systemctl stop "${1}" &> /dev/null || true + else + service "${1}" stop &> /dev/null || true + fi + echo " done." +} + +start_service() { + # Start/Restart service passed in as argument + # This should not fail, it's an error if it does + echo ":::" + echo -n "::: Starting ${1} service..." + if [ -x "$(command -v systemctl)" ]; then + systemctl restart "${1}" &> /dev/null + else + service "${1}" restart &> /dev/null + fi + echo " done." +} + +enable_service() { + # Enable service so that it will start with next reboot + echo ":::" + echo -n "::: Enabling ${1} service to start on reboot..." + if [ -x "$(command -v systemctl)" ]; then + systemctl enable "${1}" &> /dev/null + else + update-rc.d "${1}" defaults &> /dev/null + fi + echo " done." +} + +update_pacakge_cache() { + #Running apt-get update/upgrade with minimal output can cause some issues with + #requiring user input (e.g password for phpmyadmin see #218) + + #Check to see if apt-get update has already been run today + #it needs to have been run at least once on new installs! + timestamp=$(stat -c %Y ${PKG_CACHE}) + timestampAsDate=$(date -d @"${timestamp}" "+%b %e") + today=$(date "+%b %e") + + if [ ! "${today}" == "${timestampAsDate}" ]; then + #update package lists + echo ":::" + echo -n "::: ${PKG_MANAGER} update has not been run today. Running now..." + ${UPDATE_PKG_CACHE} &> /dev/null + echo " done!" + fi +} + +notify_package_updates_available() { + # Let user know if they have outdated packages on their system and + # advise them to run a package update at soonest possible. + echo ":::" + echo -n "::: Checking ${PKG_MANAGER} for upgraded packages...." + updatesToInstall=$(eval "${PKG_COUNT}") + echo " done!" + echo ":::" + if [[ ${updatesToInstall} -eq "0" ]]; then + echo "::: Your system is up to date! Continuing with Pi-hole installation..." + else + echo "::: There are ${updatesToInstall} updates available for your system!" + echo "::: We recommend you run '${PKG_UPDATE}' after installing Pi-Hole! " + echo ":::" + fi +} + +install_dependent_packages() { + # Install packages passed in via argument array + # No spinner - conflicts with set -e + declare -a argArray1=("${!1}") + + if command -v debconf-apt-progress &> /dev/null; then + debconf-apt-progress -- ${PKG_INSTALL} "${argArray1[@]}" + else + for i in "${argArray1[@]}"; do + echo -n "::: Checking for $i..." + package_check_install "${i}" &> /dev/null + echo " installed!" + done + fi +} + +CreateLogFile() { + # Create logfiles if necessary + echo ":::" + echo -n "::: Creating log file and changing owner to dnsmasq..." + if [ ! -f /var/log/pihole.log ]; then + touch /var/log/pihole.log + chmod 644 /var/log/pihole.log + chown "${DNSMASQ_USER}":root /var/log/pihole.log + echo " done!" + else + echo " already exists!" + fi +} + +installPiholeWeb() { + # Install the web interface + echo ":::" + echo "::: Installing pihole custom index page..." + if [ -d "/var/www/html/pihole" ]; then + if [ -f "/var/www/html/pihole/index.html" ]; then + echo "::: Existing index.html detected, not overwriting" + else + echo -n "::: index.html missing, replacing... " + cp /etc/.pihole/advanced/index.html /var/www/html/pihole/ + echo " done!" + fi + + if [ -f "/var/www/html/pihole/index.js" ]; then + echo "::: Existing index.js detected, not overwriting" + else + echo -n "::: index.js missing, replacing... " + cp /etc/.pihole/advanced/index.js /var/www/html/pihole/ + echo " done!" + fi + + else + mkdir /var/www/html/pihole + if [ -f /var/www/html/index.lighttpd.html ]; then + mv /var/www/html/index.lighttpd.html /var/www/html/index.lighttpd.orig + else + printf "\n:::\tNo default index.lighttpd.html file found... not backing up" + fi + cp /etc/.pihole/advanced/index.* /var/www/html/pihole/. + echo " done!" + fi + # Install Sudoer file + echo ":::" + echo -n "::: Installing sudoer file..." + mkdir -p /etc/sudoers.d/ + cp /etc/.pihole/advanced/pihole.sudo /etc/sudoers.d/pihole + chmod 0440 /etc/sudoers.d/pihole + echo " done!" +} + +installCron() { + # Install the cron job + echo ":::" + echo -n "::: Installing latest Cron script..." + cp /etc/.pihole/advanced/pihole.cron /etc/cron.d/pihole + echo " done!" +} + +runGravity() { + # Run gravity.sh to build blacklists + echo ":::" + echo "::: Preparing to run gravity.sh to refresh hosts..." + if ls /etc/pihole/list* 1> /dev/null 2>&1; then + echo "::: Cleaning up previous install (preserving whitelist/blacklist)" + rm /etc/pihole/list.* + fi + echo "::: Running gravity.sh" + /opt/pihole/gravity.sh +} + +create_pihole_user() { + # Check if user pihole exists and create if not + echo "::: Checking if user 'pihole' exists..." + id -u pihole &> /dev/null && echo "::: User 'pihole' already exists" || (echo "::: User 'pihole' doesn't exist. Creating..." && useradd -r -s /usr/sbin/nologin pihole) +} + +configureFirewall() { + # Allow HTTP and DNS traffic + if [ -x "$(command -v firewall-cmd)" ]; then + firewall-cmd --state &> /dev/null && ( echo "::: Configuring firewalld for httpd and dnsmasq.." && firewall-cmd --permanent --add-port=80/tcp && firewall-cmd --permanent --add-port=53/tcp \ + && firewall-cmd --permanent --add-port=53/udp && firewall-cmd --reload) || echo "::: FirewallD not enabled" + elif [ -x "$(command -v iptables)" ]; then + echo "::: Configuring iptables for httpd and dnsmasq.." + iptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT + iptables -A INPUT -p tcp -m tcp --dport 53 -j ACCEPT + iptables -A INPUT -p udp -m udp --dport 53 -j ACCEPT + else + echo "::: No firewall detected.. skipping firewall configuration." + fi +} + +finalExports() { + # Update variables in setupVars.conf file + if [ -e "${setupVars}" ]; then + sed -i.update.bak '/PIHOLE_INTERFACE/d;/IPV4_ADDRESS/d;/IPV6_ADDRESS/d;/PIHOLE_DNS_1/d;/PIHOLE_DNS_2/d;/QUERY_LOGGING/d;' "${setupVars}" + fi + { + echo "PIHOLE_INTERFACE=${PIHOLE_INTERFACE}" + echo "IPV4_ADDRESS=${IPV4_ADDRESS}" + echo "IPV6_ADDRESS=${IPV6_ADDRESS}" + echo "PIHOLE_DNS_1=${PIHOLE_DNS_1}" + echo "PIHOLE_DNS_2=${PIHOLE_DNS_2}" + echo "QUERY_LOGGING=${QUERY_LOGGING}" + }>> "${setupVars}" +} + +installPihole() { + # Install base files and web interface + create_pihole_user + if [ ! -d "/var/www/html" ]; then + mkdir -p /var/www/html + fi + chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/www/html + chmod 775 /var/www/html + usermod -a -G ${LIGHTTPD_GROUP} pihole + if [ -x "$(command -v lighty-enable-mod)" ]; then + lighty-enable-mod fastcgi fastcgi-php > /dev/null || true + else + printf "\n:::\tWarning: 'lighty-enable-mod' utility not found. Please ensure fastcgi is enabled if you experience issues.\n" + fi + installScripts + installConfigs + CreateLogFile + configureSelinux + installPiholeWeb + installCron + configureFirewall + finalExports + runGravity +} + +accountForRefactor() { + # At some point in the future this list can be pruned, for now we'll need it to ensure updates don't break. + + # Refactoring of install script has changed the name of a couple of variables. Sort them out here. + + sed -i 's/piholeInterface/PIHOLE_INTERFACE/g' ${setupVars} + sed -i 's/IPv4_address/IPV4_ADDRESS/g' ${setupVars} + sed -i 's/IPv4addr/IPV4_ADDRESS/g' ${setupVars} + sed -i 's/IPv6_address/IPV6_ADDRESS/g' ${setupVars} + sed -i 's/piholeIPv6/IPV6_ADDRESS/g' ${setupVars} + sed -i 's/piholeDNS1/PIHOLE_DNS_1/g' ${setupVars} + sed -i 's/piholeDNS2/PIHOLE_DNS_2/g' ${setupVars} + +} + +updatePihole() { + accountForRefactor + # Source ${setupVars} for use in the rest of the functions. + . ${setupVars} + # Install base files and web interface + installScripts + installConfigs + CreateLogFile + configureSelinux + installPiholeWeb + installCron + configureFirewall + finalExports #re-export setupVars.conf to account for any new vars added in new versions + runGravity +} + +configureSelinux() { + if [ -x "$(command -v getenforce)" ]; then + printf "\n::: SELinux Detected\n" + printf ":::\tChecking for SELinux policy development packages..." + package_check_install "selinux-policy-devel" > /dev/null + echo " installed!" + printf ":::\tEnabling httpd server side includes (SSI).. " + setsebool -P httpd_ssi_exec on &> /dev/null && echo "Success" || echo "SELinux not enabled" + printf "\n:::\tCompiling Pi-Hole SELinux policy..\n" + if ! [ -x "$(command -v systemctl)" ]; then + sed -i.bak '/systemd/d' /etc/.pihole/advanced/selinux/pihole.te + fi + checkmodule -M -m -o /etc/pihole/pihole.mod /etc/.pihole/advanced/selinux/pihole.te + semodule_package -o /etc/pihole/pihole.pp -m /etc/pihole/pihole.mod + semodule -i /etc/pihole/pihole.pp + rm -f /etc/pihole/pihole.mod + semodule -l | grep pihole &> /dev/null && echo "::: Installed Pi-Hole SELinux policy" || echo "::: Warning: Pi-Hole SELinux policy did not install." + fi +} + +displayFinalMessage() { + if (( ${#1} > 0 )) ; then + # Final completion message to user + whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: + +IPv4: ${IPV4_ADDRESS%/*} +IPv6: ${IPV6_ADDRESS} + +If you set a new IP address, you should restart the Pi. + +The install log is in /etc/pihole. +View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin +The currently set password is ${1}" ${r} ${c} + else + whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: + +IPv4: ${IPV4_ADDRESS%/*} +IPv6: ${IPV6_ADDRESS} + +If you set a new IP address, you should restart the Pi. + +The install log is in /etc/pihole. +View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin" ${r} ${c} + fi +} + +update_dialogs() { + # reconfigure + if [ "${reconfigure}" = true ]; then + opt1a="Repair" + opt1b="This will retain existing settings" + strAdd="You will remain on the same version" + else + opt1a="Update" + opt1b="This will retain existing settings." + strAdd="You will be updated to the latest version." + fi + opt2a="Reconfigure" + opt2b="This will allow you to enter new settings" + + UpdateCmd=$(whiptail --title "Existing Install Detected!" --menu "\n\nWe have detected an existing install.\n\nPlease choose from the following options: \n($strAdd)" ${r} ${c} 2 \ + "${opt1a}" "${opt1b}" \ + "${opt2a}" "${opt2b}" 3>&2 2>&1 1>&3) + + if [[ $? = 0 ]];then + case ${UpdateCmd} in + ${opt1a}) + echo "::: ${opt1a} option selected." + useUpdateVars=true + ;; + ${opt2a}) + echo "::: ${opt2a} option selected" + useUpdateVars=false + ;; + esac + else + echo "::: Cancel selected. Exiting..." + exit 1 + fi + +} + +main() { +# Check arguments for the undocumented flags + for var in "$@"; do + case "$var" in + "--reconfigure" ) reconfigure=true;; + "--i_do_not_follow_recommendations" ) skipSpaceCheck=false;; + "--unattended" ) runUnattended=true;; + esac + done + + if [[ -f ${setupVars} ]]; then + if [[ "${runUnattended}" == true ]]; then + echo "::: --unattended passed to install script, no whiptail dialogs will be displayed" + useUpdateVars=true + else + update_dialogs + fi + fi + + # Start the installer + # Verify there is enough disk space for the install + if [[ "${skipSpaceCheck}" == true ]]; then + echo "::: --i_do_not_follow_recommendations passed to script, skipping free disk space verification!" + else + verifyFreeDiskSpace + fi + + # Update package cache + update_pacakge_cache + + # Notify user of package availability + notify_package_updates_available + + # Install packages used by this installation script + install_dependent_packages INSTALLER_DEPS[@] + + if [[ "${reconfigure}" == true ]]; then + echo "::: --reconfigure passed to install script. Not downloading/updating local repos" + else + # Get Git files for Core and Admin + getGitFiles ${PI_HOLE_LOCAL_REPO} ${piholeGitUrl} + getGitFiles ${webInterfaceDir} ${webInterfaceGitUrl} + fi + + if [[ ${useUpdateVars} == false ]]; then + # Display welcome dialogs + welcomeDialogs + # Create directory for Pi-hole storage + mkdir -p /etc/pihole/ + # Remove legacy scripts from previous storage location + remove_legacy_scripts + # Stop resolver and webserver while installing proceses + stop_service dnsmasq + stop_service lighttpd + # Determine available interfaces + get_available_interfaces + # Find interfaces and let the user choose one + chooseInterface + # Let the user decide if they want to block ads over IPv4 and/or IPv6 + use4andor6 + # Decide what upstream DNS Servers to use + setDNS + # Let the user decide if they want query logging enabled... + setLogging + + # Install packages used by the Pi-hole + install_dependent_packages PIHOLE_DEPS[@] + + # Install and log everything to a file + installPihole | tee ${tmpLog} + else + # update packages used by the Pi-hole + install_dependent_packages PIHOLE_DEPS[@] + + updatePihole | tee ${tmpLog} + fi + + # Move the log file into /etc/pihole for storage + mv ${tmpLog} ${instalLogLoc} + + # Add password to web UI if there is none + pw="" + if [[ $(grep 'WEBPASSWORD' -c /etc/pihole/setupVars.conf) == 0 ]] ; then + pw=$(tr -dc _A-Z-a-z-0-9 < /dev/urandom | head -c 8) + pihole -a -p ${pw} + fi + + if [[ "${useUpdateVars}" == false ]]; then + displayFinalMessage ${pw} + fi + + echo "::: Restarting services..." + # Start services + start_service dnsmasq + enable_service dnsmasq + start_service lighttpd + enable_service lighttpd + echo "::: done." + + echo ":::" + if [[ "${useUpdateVars}" == false ]]; then + echo "::: Installation Complete! Configure your devices to use the Pi-hole as their DNS server using:" + echo "::: ${IPV4_ADDRESS%/*}" + echo "::: ${IPV6_ADDRESS}" + echo ":::" + echo "::: If you set a new IP address, you should restart the Pi." + echo "::: View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin" + else + echo "::: Update complete!" + fi + + if (( ${#pw} > 0 )) ; then + echo ":::" + echo "::: Note: As security measure a password has been installed for your web interface" + echo "::: The currently set password is" + echo "::: ${pw}" + echo ":::" + echo "::: You can always change it using" + echo "::: pihole -a -p new_password" + fi + + echo ":::" + echo "::: The install log is located at: /etc/pihole/install.log" +} + +if [[ "${PH_TEST}" != true ]] ; then + main "$@" +fi From c72ffae4a2c7bc234d08b9168db482727419de8d Mon Sep 17 00:00:00 2001 From: Dan Schaper Date: Tue, 20 Dec 2016 17:02:51 -0800 Subject: [PATCH 28/89] Don't install extra packages (openresolv was installed over resolvconf). No longer need --fix-missing. Signed-off-by: Dan Schaper --- automated install/basic-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 0c66094f..55c4716c 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -81,7 +81,7 @@ if [[ $(command -v apt-get) ]]; then PKG_CACHE="/var/lib/apt/lists/" UPDATE_PKG_CACHE="${PKG_MANAGER} update" PKG_UPDATE="${PKG_MANAGER} upgrade" - PKG_INSTALL="${PKG_MANAGER} --yes --fix-missing install" + PKG_INSTALL="${PKG_MANAGER} --yes --no-install-recommends install" # grep -c will return 1 retVal on 0 matches, block this throwing the set -e with an OR TRUE PKG_COUNT="${PKG_MANAGER} -s -o Debug::NoLocking=true upgrade | grep -c ^Inst || true" # ######################################### From 156a51c945f3c2e537347be467d5a8aabb26c49d Mon Sep 17 00:00:00 2001 From: Dan Schaper Date: Tue, 20 Dec 2016 17:04:46 -0800 Subject: [PATCH 29/89] Remove extra display only variable. Signed-off-by: Dan Schaper --- automated install/basic-install.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 55c4716c..e10fa196 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -80,7 +80,6 @@ if [[ $(command -v apt-get) ]]; then PKG_MANAGER="apt-get" PKG_CACHE="/var/lib/apt/lists/" UPDATE_PKG_CACHE="${PKG_MANAGER} update" - PKG_UPDATE="${PKG_MANAGER} upgrade" PKG_INSTALL="${PKG_MANAGER} --yes --no-install-recommends install" # grep -c will return 1 retVal on 0 matches, block this throwing the set -e with an OR TRUE PKG_COUNT="${PKG_MANAGER} -s -o Debug::NoLocking=true upgrade | grep -c ^Inst || true" @@ -110,7 +109,6 @@ elif [ $(command -v rpm) ]; then fi PKG_CACHE="/var/cache/${PKG_MANAGER}" UPDATE_PKG_CACHE="${PKG_MANAGER} check-update" - PKG_UPDATE="${PKG_MANAGER} update -y" PKG_INSTALL="${PKG_MANAGER} install -y" PKG_COUNT="${PKG_MANAGER} check-update | egrep '(.i686|.x86|.noarch|.arm|.src)' | wc -l" INSTALLER_DEPS=(git iproute net-tools newt procps-ng) @@ -744,7 +742,7 @@ notify_package_updates_available() { echo "::: Your system is up to date! Continuing with Pi-hole installation..." else echo "::: There are ${updatesToInstall} updates available for your system!" - echo "::: We recommend you run '${PKG_UPDATE}' after installing Pi-Hole! " + echo "::: We recommend you update your OS after installing Pi-Hole! " echo ":::" fi } From ff2783f9fc884e24d2e8d88918f4aeb01ffeb985 Mon Sep 17 00:00:00 2001 From: Dan Schaper Date: Tue, 20 Dec 2016 17:22:57 -0800 Subject: [PATCH 30/89] Tabs to spaces and formatting. Signed-off-by: Dan Schaper --- automated install/basic-install.sh | 1502 ++++++++++++++-------------- 1 file changed, 749 insertions(+), 753 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index a04cac3f..3cd00ac1 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -158,393 +158,391 @@ is_repo() { make_repo() { local directory="${1}" - local remoteRepo="${2}" - # Remove the non-repod interface and clone the interface - echo -n "::: Cloning $remoteRepo into $directory..." - rm -rf "${directory}" - git clone -q --depth 1 "${remoteRepo}" "${directory}" > /dev/null & spinner $! - echo " done!" + local remoteRepo="${2}" + # Remove the non-repod interface and clone the interface + echo -n "::: Cloning $remoteRepo into $directory..." + rm -rf "${directory}" + git clone -q --depth 1 "${remoteRepo}" "${directory}" > /dev/null & spinner $! + echo " done!" } update_repo() { - local directory="${1}" - # Pull the latest commits - echo -n "::: Updating repo in $1..." - cd "${directory}" || exit 1 - git stash -q > /dev/null & spinner $! - git pull -q > /dev/null & spinner $! - echo " done!" + local directory="${1}" + # Pull the latest commits + echo -n "::: Updating repo in $1..." + cd "${directory}" || exit 1 + git stash -q > /dev/null & spinner $! + git pull -q > /dev/null & spinner $! + echo " done!" } getGitFiles() { - # Setup git repos for directory and repository passed - # as arguments 1 and 2 - local directory="${1}" - local remoteRepo="${2}" - echo ":::" - echo "::: Checking for existing repository..." - if is_repo "${directory}"; then - update_repo "${directory}" - else - make_repo "${directory}" "${remoteRepo}" - fi + # Setup git repos for directory and repository passed + # as arguments 1 and 2 + local directory="${1}" + local remoteRepo="${2}" + echo ":::" + echo "::: Checking for existing repository..." + if is_repo "${directory}"; then + update_repo "${directory}" + else + make_repo "${directory}" "${remoteRepo}" + fi } find_IPv4_information() { - # Find IP used to route to outside world - IPv4dev=$(ip route get 8.8.8.8 | awk '{for(i=1;i<=NF;i++)if($i~/dev/)print $(i+1)}') - IPV4_ADDRESS=$(ip -o -f inet addr show dev "$IPv4dev" | awk '{print $4}' | awk 'END {print}') - IPv4gw=$(ip route get 8.8.8.8 | awk '{print $3}') + # Find IP used to route to outside world + IPv4dev=$(ip route get 8.8.8.8 | awk '{for(i=1;i<=NF;i++)if($i~/dev/)print $(i+1)}') + IPV4_ADDRESS=$(ip -o -f inet addr show dev "$IPv4dev" | awk '{print $4}' | awk 'END {print}') + IPv4gw=$(ip route get 8.8.8.8 | awk '{print $3}') } get_available_interfaces() { - # Get available interfaces. Consider only getting UP interfaces in the future, and leaving DOWN interfaces out of list. - availableInterfaces=$(ip -o link | awk '{print $2}' | grep -v "lo" | cut -d':' -f1 | cut -d'@' -f1) + # Get available interfaces. Consider only getting UP interfaces in the future, and leaving DOWN interfaces out of list. + availableInterfaces=$(ip -o link | awk '{print $2}' | grep -v "lo" | cut -d':' -f1 | cut -d'@' -f1) } welcomeDialogs() { - # Display the welcome dialog - whiptail --msgbox --backtitle "Welcome" --title "Pi-hole automated installer" "\n\nThis installer will transform your device into a network-wide ad blocker!" ${r} ${c} + # Display the welcome dialog + whiptail --msgbox --backtitle "Welcome" --title "Pi-hole automated installer" "\n\nThis installer will transform your device into a network-wide ad blocker!" ${r} ${c} - # Support for a part-time dev - whiptail --msgbox --backtitle "Plea" --title "Free and open source" "\n\nThe Pi-hole is free, but powered by your donations: http://pi-hole.net/donate" ${r} ${c} + # Support for a part-time dev + whiptail --msgbox --backtitle "Plea" --title "Free and open source" "\n\nThe Pi-hole is free, but powered by your donations: http://pi-hole.net/donate" ${r} ${c} - # Explain the need for a static address - whiptail --msgbox --backtitle "Initiating network interface" --title "Static IP Needed" "\n\nThe Pi-hole is a SERVER so it needs a STATIC IP ADDRESS to function properly. + # Explain the need for a static address + whiptail --msgbox --backtitle "Initiating network interface" --title "Static IP Needed" "\n\nThe Pi-hole is a SERVER so it needs a STATIC IP ADDRESS to function properly. In the next section, you can choose to use your current network settings (DHCP) or to manually edit them." ${r} ${c} } verifyFreeDiskSpace() { - # 50MB is the minimum space needed (45MB install (includes web admin bootstrap/jquery libraries etc) + 5MB one day of logs.) - # - Fourdee: Local ensures the variable is only created, and accessible within this function/void. Generally considered a "good" coding practice for non-global variables. - echo "::: Verifying free disk space..." - local required_free_kilobytes=51200 - local existing_free_kilobytes=$(df -Pk | grep -m1 '\/$' | awk '{print $4}') + # 50MB is the minimum space needed (45MB install (includes web admin bootstrap/jquery libraries etc) + 5MB one day of logs.) + # - Fourdee: Local ensures the variable is only created, and accessible within this function/void. Generally considered a "good" coding practice for non-global variables. + echo "::: Verifying free disk space..." + local required_free_kilobytes=51200 + local existing_free_kilobytes=$(df -Pk | grep -m1 '\/$' | awk '{print $4}') - # - Unknown free disk space , not a integer - if ! [[ "${existing_free_kilobytes}" =~ ^([0-9])+$ ]]; then - echo "::: Unknown free disk space!" - echo "::: We were unable to determine available free disk space on this system." - echo "::: You may override this check and force the installation, however, it is not recommended" - echo "::: To do so, pass the argument '--i_do_not_follow_recommendations' to the install script" - echo "::: eg. curl -L https://install.pi-hole.net | bash /dev/stdin --i_do_not_follow_recommendations" - exit 1 - # - Insufficient free disk space - elif [[ ${existing_free_kilobytes} -lt ${required_free_kilobytes} ]]; then - echo "::: Insufficient Disk Space!" - echo "::: Your system appears to be low on disk space. pi-hole recommends a minimum of $required_free_kilobytes KiloBytes." - echo "::: You only have ${existing_free_kilobytes} KiloBytes free." - echo "::: If this is a new install you may need to expand your disk." - echo "::: Try running 'sudo raspi-config', and choose the 'expand file system option'" - echo "::: After rebooting, run this installation again. (curl -L https://install.pi-hole.net | bash)" - - echo "Insufficient free space, exiting..." - exit 1 - - fi + # - Unknown free disk space , not a integer + if ! [[ "${existing_free_kilobytes}" =~ ^([0-9])+$ ]]; then + echo "::: Unknown free disk space!" + echo "::: We were unable to determine available free disk space on this system." + echo "::: You may override this check and force the installation, however, it is not recommended" + echo "::: To do so, pass the argument '--i_do_not_follow_recommendations' to the install script" + echo "::: eg. curl -L https://install.pi-hole.net | bash /dev/stdin --i_do_not_follow_recommendations" + exit 1 + # - Insufficient free disk space + elif [[ ${existing_free_kilobytes} -lt ${required_free_kilobytes} ]]; then + echo "::: Insufficient Disk Space!" + echo "::: Your system appears to be low on disk space. pi-hole recommends a minimum of $required_free_kilobytes KiloBytes." + echo "::: You only have ${existing_free_kilobytes} KiloBytes free." + echo "::: If this is a new install you may need to expand your disk." + echo "::: Try running 'sudo raspi-config', and choose the 'expand file system option'" + echo "::: After rebooting, run this installation again. (curl -L https://install.pi-hole.net | bash)" + echo "Insufficient free space, exiting..." + exit 1 + fi } chooseInterface() { - # Turn the available interfaces into an array so it can be used with a whiptail dialog - local interfacesArray=() - # Number of available interfaces - local interfaceCount - # Whiptail variable storage - local chooseInterfaceCmd - # Temporary Whiptail options storage - local chooseInterfaceOptions - # Loop sentinel variable - local firstLoop=1 + # Turn the available interfaces into an array so it can be used with a whiptail dialog + local interfacesArray=() + # Number of available interfaces + local interfaceCount + # Whiptail variable storage + local chooseInterfaceCmd + # Temporary Whiptail options storage + local chooseInterfaceOptions + # Loop sentinel variable + local firstLoop=1 - while read -r line; do - mode="OFF" - if [[ ${firstLoop} -eq 1 ]]; then - firstLoop=0 - mode="ON" - fi - interfacesArray+=("${line}" "available" "${mode}") - done <<< "${availableInterfaces}" + while read -r line; do + mode="OFF" + if [[ ${firstLoop} -eq 1 ]]; then + firstLoop=0 + mode="ON" + fi + interfacesArray+=("${line}" "available" "${mode}") + done <<< "${availableInterfaces}" - # Find out how many interfaces are available to choose from - interfaceCount=$(echo "${availableInterfaces}" | wc -l) - chooseInterfaceCmd=(whiptail --separate-output --radiolist "Choose An Interface (press space to select)" ${r} ${c} ${interfaceCount}) - chooseInterfaceOptions=$("${chooseInterfaceCmd[@]}" "${interfacesArray[@]}" 2>&1 >/dev/tty) - if [[ $? = 0 ]]; then - for desiredInterface in ${chooseInterfaceOptions}; do - PIHOLE_INTERFACE=${desiredInterface} - echo "::: Using interface: $PIHOLE_INTERFACE" - done - else - echo "::: Cancel selected, exiting...." - exit 1 - fi + # Find out how many interfaces are available to choose from + interfaceCount=$(echo "${availableInterfaces}" | wc -l) + chooseInterfaceCmd=(whiptail --separate-output --radiolist "Choose An Interface (press space to select)" ${r} ${c} ${interfaceCount}) + chooseInterfaceOptions=$("${chooseInterfaceCmd[@]}" "${interfacesArray[@]}" 2>&1 >/dev/tty) + if [[ $? = 0 ]]; then + for desiredInterface in ${chooseInterfaceOptions}; do + PIHOLE_INTERFACE=${desiredInterface} + echo "::: Using interface: $PIHOLE_INTERFACE" + done + else + echo "::: Cancel selected, exiting...." + exit 1 + fi } useIPv6dialog() { - # Show the IPv6 address used for blocking - IPV6_ADDRESS=$(ip -6 route get 2001:4860:4860::8888 | awk -F " " '{ for(i=1;i<=NF;i++) if ($i == "src") print $(i+1) }') - whiptail --msgbox --backtitle "IPv6..." --title "IPv6 Supported" "$IPV6_ADDRESS will be used to block ads." ${r} ${c} + # Show the IPv6 address used for blocking + IPV6_ADDRESS=$(ip -6 route get 2001:4860:4860::8888 | awk -F " " '{ for(i=1;i<=NF;i++) if ($i == "src") print $(i+1) }') + whiptail --msgbox --backtitle "IPv6..." --title "IPv6 Supported" "$IPV6_ADDRESS will be used to block ads." ${r} ${c} } use4andor6() { - local useIPv4 - local useIPv6 - # Let use select IPv4 and/or IPv6 - cmd=(whiptail --separate-output --checklist "Select Protocols (press space to select)" ${r} ${c} 2) - options=(IPv4 "Block ads over IPv4" on - IPv6 "Block ads over IPv6" on) - choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty) - if [[ $? = 0 ]];then - for choice in ${choices} - do - case ${choice} in - IPv4 ) useIPv4=true;; - IPv6 ) useIPv6=true;; - esac - done - if [[ ${useIPv4} ]]; then - find_IPv4_information - getStaticIPv4Settings - setStaticIPv4 - fi - if [[ ${useIPv6} ]]; then - useIPv6dialog - fi - echo "::: IPv4 address: ${IPV4_ADDRESS}" - echo "::: IPv6 address: ${IPV6_ADDRESS}" - if [ ! ${useIPv4} ] && [ ! ${useIPv6} ]; then - echo "::: Cannot continue, neither IPv4 or IPv6 selected" - echo "::: Exiting" - exit 1 - fi - else - echo "::: Cancel selected. Exiting..." - exit 1 - fi + local useIPv4 + local useIPv6 + # Let use select IPv4 and/or IPv6 + cmd=(whiptail --separate-output --checklist "Select Protocols (press space to select)" ${r} ${c} 2) + options=(IPv4 "Block ads over IPv4" on + IPv6 "Block ads over IPv6" on) + choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty) + if [[ $? = 0 ]];then + for choice in ${choices} + do + case ${choice} in + IPv4 ) useIPv4=true;; + IPv6 ) useIPv6=true;; + esac + done + if [[ ${useIPv4} ]]; then + find_IPv4_information + getStaticIPv4Settings + setStaticIPv4 + fi + if [[ ${useIPv6} ]]; then + useIPv6dialog + fi + echo "::: IPv4 address: ${IPV4_ADDRESS}" + echo "::: IPv6 address: ${IPV6_ADDRESS}" + if [ ! ${useIPv4} ] && [ ! ${useIPv6} ]; then + echo "::: Cannot continue, neither IPv4 or IPv6 selected" + echo "::: Exiting" + exit 1 + fi + else + echo "::: Cancel selected. Exiting..." + exit 1 + fi } getStaticIPv4Settings() { - # Ask if the user wants to use DHCP settings as their static IP - if (whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Do you want to use your current network settings as a static address? - IP address: ${IPV4_ADDRESS} - Gateway: ${IPv4gw}" ${r} ${c}); then - # If they choose yes, let the user know that the IP address will not be available via DHCP and may cause a conflict. - whiptail --msgbox --backtitle "IP information" --title "FYI: IP Conflict" "It is possible your router could still try to assign this IP to a device, which would cause a conflict. But in most cases the router is smart enough to not do that. + # Ask if the user wants to use DHCP settings as their static IP + if (whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Do you want to use your current network settings as a static address? + IP address: ${IPV4_ADDRESS} + Gateway: ${IPv4gw}" ${r} ${c}); then + # If they choose yes, let the user know that the IP address will not be available via DHCP and may cause a conflict. + whiptail --msgbox --backtitle "IP information" --title "FYI: IP Conflict" "It is possible your router could still try to assign this IP to a device, which would cause a conflict. But in most cases the router is smart enough to not do that. If you are worried, either manually set the address, or modify the DHCP reservation pool so it does not include the IP you want. It is also possible to use a DHCP reservation, but if you are going to do that, you might as well set a static address." ${r} ${c} - # Nothing else to do since the variables are already set above - else - # Otherwise, we need to ask the user to input their desired settings. - # Start by getting the IPv4 address (pre-filling it with info gathered from DHCP) - # Start a loop to let the user enter their information with the chance to go back and edit it if necessary - until [[ ${ipSettingsCorrect} = True ]]; do - # Ask for the IPv4 address - IPV4_ADDRESS=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 address" --inputbox "Enter your desired IPv4 address" ${r} ${c} "${IPV4_ADDRESS}" 3>&1 1>&2 2>&3) - if [[ $? = 0 ]]; then - echo "::: Your static IPv4 address: ${IPV4_ADDRESS}" - # Ask for the gateway - IPv4gw=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 gateway (router)" --inputbox "Enter your desired IPv4 default gateway" ${r} ${c} "${IPv4gw}" 3>&1 1>&2 2>&3) - if [[ $? = 0 ]]; then - echo "::: Your static IPv4 gateway: ${IPv4gw}" - # Give the user a chance to review their settings before moving on - if (whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Are these settings correct? - IP address: ${IPV4_ADDRESS} - Gateway: ${IPv4gw}" ${r} ${c}); then - # After that's done, the loop ends and we move on - ipSettingsCorrect=True - else - # If the settings are wrong, the loop continues - ipSettingsCorrect=False - fi - else - # Cancelling gateway settings window - ipSettingsCorrect=False - echo "::: Cancel selected. Exiting..." - exit 1 - fi - else - # Cancelling IPv4 settings window - ipSettingsCorrect=False - echo "::: Cancel selected. Exiting..." - exit 1 - fi - done - # End the if statement for DHCP vs. static - fi + # Nothing else to do since the variables are already set above + else + # Otherwise, we need to ask the user to input their desired settings. + # Start by getting the IPv4 address (pre-filling it with info gathered from DHCP) + # Start a loop to let the user enter their information with the chance to go back and edit it if necessary + until [[ ${ipSettingsCorrect} = True ]]; do + # Ask for the IPv4 address + IPV4_ADDRESS=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 address" --inputbox "Enter your desired IPv4 address" ${r} ${c} "${IPV4_ADDRESS}" 3>&1 1>&2 2>&3) + if [[ $? = 0 ]]; then + echo "::: Your static IPv4 address: ${IPV4_ADDRESS}" + # Ask for the gateway + IPv4gw=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 gateway (router)" --inputbox "Enter your desired IPv4 default gateway" ${r} ${c} "${IPv4gw}" 3>&1 1>&2 2>&3) + if [[ $? = 0 ]]; then + echo "::: Your static IPv4 gateway: ${IPv4gw}" + # Give the user a chance to review their settings before moving on + if (whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Are these settings correct? + IP address: ${IPV4_ADDRESS} + Gateway: ${IPv4gw}" ${r} ${c}); then + # After that's done, the loop ends and we move on + ipSettingsCorrect=True + else + # If the settings are wrong, the loop continues + ipSettingsCorrect=False + fi + else + # Cancelling gateway settings window + ipSettingsCorrect=False + echo "::: Cancel selected. Exiting..." + exit 1 + fi + else + # Cancelling IPv4 settings window + ipSettingsCorrect=False + echo "::: Cancel selected. Exiting..." + exit 1 + fi + done + # End the if statement for DHCP vs. static + fi } setDHCPCD() { - # Append these lines to dhcpcd.conf to enable a static IP - echo "## interface ${PIHOLE_INTERFACE} - static ip_address=${IPV4_ADDRESS} - static routers=${IPv4gw} - static domain_name_servers=${IPv4gw}" | tee -a /etc/dhcpcd.conf >/dev/null + # Append these lines to dhcpcd.conf to enable a static IP + echo "## interface ${PIHOLE_INTERFACE} + static ip_address=${IPV4_ADDRESS} + static routers=${IPv4gw} + static domain_name_servers=${IPv4gw}" | tee -a /etc/dhcpcd.conf >/dev/null } setStaticIPv4() { - local IFCFG_FILE - local IPADDR - local CIDR - if [[ -f /etc/dhcpcd.conf ]]; then - # Debian Family - if grep -q "${IPV4_ADDRESS}" /etc/dhcpcd.conf; then - echo "::: Static IP already configured" - else - setDHCPCD - ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" - echo ":::" - echo "::: Setting IP to ${IPV4_ADDRESS}. You may need to restart after the install is complete." - echo ":::" - fi - elif [[ -f /etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE} ]];then - # Fedora Family - IFCFG_FILE=/etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE} - if grep -q "${IPV4_ADDRESS}" "${IFCFG_FILE}"; then - echo "::: Static IP already configured" - else - IPADDR=$(echo "${IPV4_ADDRESS}" | cut -f1 -d/) - CIDR=$(echo "${IPV4_ADDRESS}" | cut -f2 -d/) - # Backup existing interface configuration: - cp "${IFCFG_FILE}" "${IFCFG_FILE}".pihole.orig - # Build Interface configuration file: - { - echo "# Configured via Pi-Hole installer" - echo "DEVICE=$PIHOLE_INTERFACE" - echo "BOOTPROTO=none" - echo "ONBOOT=yes" - echo "IPADDR=$IPADDR" - echo "PREFIX=$CIDR" - echo "GATEWAY=$IPv4gw" - echo "DNS1=$PIHOLE_DNS_1" - echo "DNS2=$PIHOLE_DNS_2" - echo "USERCTL=no" - }> "${IFCFG_FILE}" - ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" - if [ -x "$(command -v nmcli)" ];then - # Tell NetworkManager to read our new sysconfig file - nmcli con load "${IFCFG_FILE}" > /dev/null - fi - echo ":::" - echo "::: Setting IP to ${IPV4_ADDRESS}. You may need to restart after the install is complete." - echo ":::" - fi - else - echo "::: Warning: Unable to locate configuration file to set static IPv4 address!" - exit 1 - fi + local IFCFG_FILE + local IPADDR + local CIDR + if [[ -f /etc/dhcpcd.conf ]]; then + # Debian Family + if grep -q "${IPV4_ADDRESS}" /etc/dhcpcd.conf; then + echo "::: Static IP already configured" + else + setDHCPCD + ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" + echo ":::" + echo "::: Setting IP to ${IPV4_ADDRESS}. You may need to restart after the install is complete." + echo ":::" + fi + elif [[ -f /etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE} ]];then + # Fedora Family + IFCFG_FILE=/etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE} + if grep -q "${IPV4_ADDRESS}" "${IFCFG_FILE}"; then + echo "::: Static IP already configured" + else + IPADDR=$(echo "${IPV4_ADDRESS}" | cut -f1 -d/) + CIDR=$(echo "${IPV4_ADDRESS}" | cut -f2 -d/) + # Backup existing interface configuration: + cp "${IFCFG_FILE}" "${IFCFG_FILE}".pihole.orig + # Build Interface configuration file: + { + echo "# Configured via Pi-Hole installer" + echo "DEVICE=$PIHOLE_INTERFACE" + echo "BOOTPROTO=none" + echo "ONBOOT=yes" + echo "IPADDR=$IPADDR" + echo "PREFIX=$CIDR" + echo "GATEWAY=$IPv4gw" + echo "DNS1=$PIHOLE_DNS_1" + echo "DNS2=$PIHOLE_DNS_2" + echo "USERCTL=no" + }> "${IFCFG_FILE}" + ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" + if [ -x "$(command -v nmcli)" ];then + # Tell NetworkManager to read our new sysconfig file + nmcli con load "${IFCFG_FILE}" > /dev/null + fi + echo ":::" + echo "::: Setting IP to ${IPV4_ADDRESS}. You may need to restart after the install is complete." + echo ":::" + fi + else + echo "::: Warning: Unable to locate configuration file to set static IPv4 address!" + exit 1 + fi } valid_ip() { - local ip=${1} - local stat=1 + local ip=${1} + local stat=1 - if [[ ${ip} =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then - OIFS=$IFS - IFS='.' - ip=(${ip}) - IFS=${OIFS} - [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \ - && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]] - stat=$? - fi - return ${stat} + if [[ ${ip} =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then + OIFS=$IFS + IFS='.' + ip=(${ip}) + IFS=${OIFS} + [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \ + && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]] + stat=$? + fi + return ${stat} } setDNS() { - DNSChooseCmd=(whiptail --separate-output --radiolist "Select Upstream DNS Provider. To use your own, select Custom." ${r} ${c} 6) - DNSChooseOptions=(Google "" on - OpenDNS "" off - Level3 "" off - Norton "" off - Comodo "" off - Custom "" off) - DNSchoices=$("${DNSChooseCmd[@]}" "${DNSChooseOptions[@]}" 2>&1 >/dev/tty) - if [[ $? = 0 ]];then - case ${DNSchoices} in - Google) - echo "::: Using Google DNS servers." - PIHOLE_DNS_1="8.8.8.8" - PIHOLE_DNS_2="8.8.4.4" - ;; - OpenDNS) - echo "::: Using OpenDNS servers." - PIHOLE_DNS_1="208.67.222.222" - PIHOLE_DNS_2="208.67.220.220" - ;; - Level3) - echo "::: Using Level3 servers." - PIHOLE_DNS_1="4.2.2.1" - PIHOLE_DNS_2="4.2.2.2" - ;; - Norton) - echo "::: Using Norton ConnectSafe servers." - PIHOLE_DNS_1="199.85.126.10" - PIHOLE_DNS_2="199.85.127.10" - ;; - Comodo) - echo "::: Using Comodo Secure servers." - PIHOLE_DNS_1="8.26.56.26" - PIHOLE_DNS_2="8.20.247.20" - ;; - Custom) - until [[ ${DNSSettingsCorrect} = True ]]; do - strInvalid="Invalid" - if [ ! ${PIHOLE_DNS_1} ]; then - if [ ! ${PIHOLE_DNS_2} ]; then - prePopulate="" - else - prePopulate=", ${PIHOLE_DNS_2}" - fi - elif [ ${PIHOLE_DNS_1} ] && [ ! ${PIHOLE_DNS_2} ]; then - prePopulate="${PIHOLE_DNS_1}" - elif [ ${PIHOLE_DNS_1} ] && [ ${PIHOLE_DNS_2} ]; then - prePopulate="${PIHOLE_DNS_1}, ${PIHOLE_DNS_2}" - fi + DNSChooseCmd=(whiptail --separate-output --radiolist "Select Upstream DNS Provider. To use your own, select Custom." ${r} ${c} 6) + DNSChooseOptions=(Google "" on + OpenDNS "" off + Level3 "" off + Norton "" off + Comodo "" off + Custom "" off) + DNSchoices=$("${DNSChooseCmd[@]}" "${DNSChooseOptions[@]}" 2>&1 >/dev/tty) + if [[ $? = 0 ]];then + case ${DNSchoices} in + Google) + echo "::: Using Google DNS servers." + PIHOLE_DNS_1="8.8.8.8" + PIHOLE_DNS_2="8.8.4.4" + ;; + OpenDNS) + echo "::: Using OpenDNS servers." + PIHOLE_DNS_1="208.67.222.222" + PIHOLE_DNS_2="208.67.220.220" + ;; + Level3) + echo "::: Using Level3 servers." + PIHOLE_DNS_1="4.2.2.1" + PIHOLE_DNS_2="4.2.2.2" + ;; + Norton) + echo "::: Using Norton ConnectSafe servers." + PIHOLE_DNS_1="199.85.126.10" + PIHOLE_DNS_2="199.85.127.10" + ;; + Comodo) + echo "::: Using Comodo Secure servers." + PIHOLE_DNS_1="8.26.56.26" + PIHOLE_DNS_2="8.20.247.20" + ;; + Custom) + until [[ ${DNSSettingsCorrect} = True ]]; do + strInvalid="Invalid" + if [ ! ${PIHOLE_DNS_1} ]; then + if [ ! ${PIHOLE_DNS_2} ]; then + prePopulate="" + else + prePopulate=", ${PIHOLE_DNS_2}" + fi + elif [ ${PIHOLE_DNS_1} ] && [ ! ${PIHOLE_DNS_2} ]; then + prePopulate="${PIHOLE_DNS_1}" + elif [ ${PIHOLE_DNS_1} ] && [ ${PIHOLE_DNS_2} ]; then + prePopulate="${PIHOLE_DNS_1}, ${PIHOLE_DNS_2}" + fi - piholeDNS=$(whiptail --backtitle "Specify Upstream DNS Provider(s)" --inputbox "Enter your desired upstream DNS provider(s), seperated by a comma.\n\nFor example '8.8.8.8, 8.8.4.4'" ${r} ${c} "${prePopulate}" 3>&1 1>&2 2>&3) + piholeDNS=$(whiptail --backtitle "Specify Upstream DNS Provider(s)" --inputbox "Enter your desired upstream DNS provider(s), seperated by a comma.\n\nFor example '8.8.8.8, 8.8.4.4'" ${r} ${c} "${prePopulate}" 3>&1 1>&2 2>&3) - if [[ $? = 0 ]]; then - PIHOLE_DNS_1=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$1}') - PIHOLE_DNS_2=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$2}') - if ! valid_ip "${PIHOLE_DNS_1}" || [ ! "${PIHOLE_DNS_1}" ]; then - PIHOLE_DNS_1=${strInvalid} - fi - if ! valid_ip "${PIHOLE_DNS_2}" && [ "${PIHOLE_DNS_2}" ]; then - PIHOLE_DNS_2=${strInvalid} - fi - else - echo "::: Cancel selected, exiting...." - exit 1 - fi - if [[ ${PIHOLE_DNS_1} == "${strInvalid}" ]] || [[ ${PIHOLE_DNS_2} == "${strInvalid}" ]]; then - whiptail --msgbox --backtitle "Invalid IP" --title "Invalid IP" "One or both entered IP addresses were invalid. Please try again.\n\n DNS Server 1: $PIHOLE_DNS_1\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c} - if [[ ${PIHOLE_DNS_1} == "${strInvalid}" ]]; then - PIHOLE_DNS_1="" - fi - if [[ ${PIHOLE_DNS_2} == "${strInvalid}" ]]; then - PIHOLE_DNS_2="" - fi - DNSSettingsCorrect=False - else - if (whiptail --backtitle "Specify Upstream DNS Provider(s)" --title "Upstream DNS Provider(s)" --yesno "Are these settings correct?\n DNS Server 1: $PIHOLE_DNS_1\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c}); then - DNSSettingsCorrect=True - else - # If the settings are wrong, the loop continues - DNSSettingsCorrect=False - fi - fi - done - ;; - esac - else - echo "::: Cancel selected. Exiting..." - exit 1 - fi + if [[ $? = 0 ]]; then + PIHOLE_DNS_1=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$1}') + PIHOLE_DNS_2=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$2}') + if ! valid_ip "${PIHOLE_DNS_1}" || [ ! "${PIHOLE_DNS_1}" ]; then + PIHOLE_DNS_1=${strInvalid} + fi + if ! valid_ip "${PIHOLE_DNS_2}" && [ "${PIHOLE_DNS_2}" ]; then + PIHOLE_DNS_2=${strInvalid} + fi + else + echo "::: Cancel selected, exiting...." + exit 1 + fi + if [[ ${PIHOLE_DNS_1} == "${strInvalid}" ]] || [[ ${PIHOLE_DNS_2} == "${strInvalid}" ]]; then + whiptail --msgbox --backtitle "Invalid IP" --title "Invalid IP" "One or both entered IP addresses were invalid. Please try again.\n\n DNS Server 1: $PIHOLE_DNS_1\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c} + if [[ ${PIHOLE_DNS_1} == "${strInvalid}" ]]; then + PIHOLE_DNS_1="" + fi + if [[ ${PIHOLE_DNS_2} == "${strInvalid}" ]]; then + PIHOLE_DNS_2="" + fi + DNSSettingsCorrect=False + else + if (whiptail --backtitle "Specify Upstream DNS Provider(s)" --title "Upstream DNS Provider(s)" --yesno "Are these settings correct?\n DNS Server 1: $PIHOLE_DNS_1\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c}); then + DNSSettingsCorrect=True + else + # If the settings are wrong, the loop continues + DNSSettingsCorrect=False + fi + fi + done + ;; + esac + else + echo "::: Cancel selected. Exiting..." + exit 1 + fi } setLogging() { @@ -552,69 +550,69 @@ setLogging() { local LogChooseOptions local LogChoices - LogToggleCommand=(whiptail --separate-output --radiolist "Do you want to log queries?\n (Disabling will render graphs on the Admin page useless):" ${r} ${c} 6) - LogChooseOptions=("On (Reccomended)" "" on - Off "" off) - LogChoices=$("${LogToggleCommand[@]}" "${LogChooseOptions[@]}" 2>&1 >/dev/tty) || (echo "::: Cancel selected. Exiting..." && exit 1) - case ${LogChoices} in - "On (Recommended)") - echo "::: Logging On." - QUERY_LOGGING=true - ;; - Off) - echo "::: Logging Off." - QUERY_LOGGING=false - ;; - esac + LogToggleCommand=(whiptail --separate-output --radiolist "Do you want to log queries?\n (Disabling will render graphs on the Admin page useless):" ${r} ${c} 6) + LogChooseOptions=("On (Reccomended)" "" on + Off "" off) + LogChoices=$("${LogToggleCommand[@]}" "${LogChooseOptions[@]}" 2>&1 >/dev/tty) || (echo "::: Cancel selected. Exiting..." && exit 1) + case ${LogChoices} in + "On (Recommended)") + echo "::: Logging On." + QUERY_LOGGING=true + ;; + Off) + echo "::: Logging Off." + QUERY_LOGGING=false + ;; + esac } version_check_dnsmasq() { - # Check if /etc/dnsmasq.conf is from pihole. If so replace with an original and install new in .d directory - local dnsmasq_conf="/etc/dnsmasq.conf" - local dnsmasq_conf_orig="/etc/dnsmasq.conf.orig" - local dnsmasq_pihole_id_string="addn-hosts=/etc/pihole/gravity.list" - local dnsmasq_original_config="/etc/.pihole/advanced/dnsmasq.conf.original" - local dnsmasq_pihole_01_snippet="/etc/.pihole/advanced/01-pihole.conf" - local dnsmasq_pihole_01_location="/etc/dnsmasq.d/01-pihole.conf" + # Check if /etc/dnsmasq.conf is from pihole. If so replace with an original and install new in .d directory + local dnsmasq_conf="/etc/dnsmasq.conf" + local dnsmasq_conf_orig="/etc/dnsmasq.conf.orig" + local dnsmasq_pihole_id_string="addn-hosts=/etc/pihole/gravity.list" + local dnsmasq_original_config="/etc/.pihole/advanced/dnsmasq.conf.original" + local dnsmasq_pihole_01_snippet="/etc/.pihole/advanced/01-pihole.conf" + local dnsmasq_pihole_01_location="/etc/dnsmasq.d/01-pihole.conf" - if [ -f ${dnsmasq_conf} ]; then - echo -n "::: Existing dnsmasq.conf found..." - if grep -q ${dnsmasq_pihole_id_string} ${dnsmasq_conf}; then - echo " it is from a previous pi-hole install." - echo -n "::: Backing up dnsmasq.conf to dnsmasq.conf.orig..." - mv -f ${dnsmasq_conf} ${dnsmasq_conf_orig} - echo " done." - echo -n "::: Restoring default dnsmasq.conf..." - cp ${dnsmasq_original_config} ${dnsmasq_conf} - echo " done." - else - echo " it is not a pi-hole file, leaving alone!" - fi - else - echo -n "::: No dnsmasq.conf found.. restoring default dnsmasq.conf..." - cp ${dnsmasq_original_config} ${dnsmasq_conf} - echo " done." - fi + if [ -f ${dnsmasq_conf} ]; then + echo -n "::: Existing dnsmasq.conf found..." + if grep -q ${dnsmasq_pihole_id_string} ${dnsmasq_conf}; then + echo " it is from a previous pi-hole install." + echo -n "::: Backing up dnsmasq.conf to dnsmasq.conf.orig..." + mv -f ${dnsmasq_conf} ${dnsmasq_conf_orig} + echo " done." + echo -n "::: Restoring default dnsmasq.conf..." + cp ${dnsmasq_original_config} ${dnsmasq_conf} + echo " done." + else + echo " it is not a pi-hole file, leaving alone!" + fi + else + echo -n "::: No dnsmasq.conf found.. restoring default dnsmasq.conf..." + cp ${dnsmasq_original_config} ${dnsmasq_conf} + echo " done." + fi - echo -n "::: Copying 01-pihole.conf to /etc/dnsmasq.d/01-pihole.conf..." - cp ${dnsmasq_pihole_01_snippet} ${dnsmasq_pihole_01_location} - echo " done." - sed -i "s/@INT@/$PIHOLE_INTERFACE/" ${dnsmasq_pihole_01_location} - if [[ "${PIHOLE_DNS_1}" != "" ]]; then - sed -i "s/@DNS1@/$PIHOLE_DNS_1/" ${dnsmasq_pihole_01_location} - else - sed -i '/^server=@DNS1@/d' ${dnsmasq_pihole_01_location} - fi - if [[ "${PIHOLE_DNS_2}" != "" ]]; then - sed -i "s/@DNS2@/$PIHOLE_DNS_2/" ${dnsmasq_pihole_01_location} - else - sed -i '/^server=@DNS2@/d' ${dnsmasq_pihole_01_location} - fi + echo -n "::: Copying 01-pihole.conf to /etc/dnsmasq.d/01-pihole.conf..." + cp ${dnsmasq_pihole_01_snippet} ${dnsmasq_pihole_01_location} + echo " done." + sed -i "s/@INT@/$PIHOLE_INTERFACE/" ${dnsmasq_pihole_01_location} + if [[ "${PIHOLE_DNS_1}" != "" ]]; then + sed -i "s/@DNS1@/$PIHOLE_DNS_1/" ${dnsmasq_pihole_01_location} + else + sed -i '/^server=@DNS1@/d' ${dnsmasq_pihole_01_location} + fi + if [[ "${PIHOLE_DNS_2}" != "" ]]; then + sed -i "s/@DNS2@/$PIHOLE_DNS_2/" ${dnsmasq_pihole_01_location} + else + sed -i '/^server=@DNS2@/d' ${dnsmasq_pihole_01_location} + fi - sed -i 's/^#conf-dir=\/etc\/dnsmasq.d$/conf-dir=\/etc\/dnsmasq.d/' ${dnsmasq_conf} + sed -i 's/^#conf-dir=\/etc\/dnsmasq.d$/conf-dir=\/etc\/dnsmasq.d/' ${dnsmasq_conf} - if [[ "${QUERY_LOGGING}" == false ]] ; then + if [[ "${QUERY_LOGGING}" == false ]] ; then #Disable Logging sed -i 's/^log-queries/#log-queries/' ${dnsmasq_pihole_01_location} else @@ -624,13 +622,13 @@ version_check_dnsmasq() { } remove_legacy_scripts() { - #Tidy up /usr/local/bin directory if installing over previous install. - oldFiles=( gravity chronometer whitelist blacklist piholeLogFlush updateDashboard uninstall setupLCD piholeDebug) - for i in "${oldFiles[@]}"; do - if [ -f "/usr/local/bin/$i.sh" ]; then - rm /usr/local/bin/"$i".sh - fi - done + #Tidy up /usr/local/bin directory if installing over previous install. + oldFiles=( gravity chronometer whitelist blacklist piholeLogFlush updateDashboard uninstall setupLCD piholeDebug) + for i in "${oldFiles[@]}"; do + if [ -f "/usr/local/bin/$i.sh" ]; then + rm /usr/local/bin/"$i".sh + fi + done } clean_existing() { @@ -639,21 +637,20 @@ clean_existing() { local clean_directory="${1}" local old_files=${2} - for script in "${old_files[@]}"; do - rm -f "${clean_directory}${script}.sh" - done - + for script in "${old_files[@]}"; do + rm -f "${clean_directory}${script}.sh" + done } installScripts() { # Install the scripts from repository to their various locations readonly install_dir="/opt/pihole/" - echo ":::" - echo -n "::: Installing scripts from ${PI_HOLE_LOCAL_REPO}..." + echo ":::" + echo -n "::: Installing scripts from ${PI_HOLE_LOCAL_REPO}..." - # Clear out script files from Pi-hole scripts directory. - clean_existing "${install_dir}" "${PI_HOLE_FILES}" + # Clear out script files from Pi-hole scripts directory. + clean_existing "${install_dir}" "${PI_HOLE_FILES}" # Install files from local core repository if is_repo "${PI_HOLE_LOCAL_REPO}"; then @@ -663,7 +660,7 @@ installScripts() { install -o "${USER}" -Dm755 -t /opt/pihole/ ./automated\ install/uninstall.sh install -o "${USER}" -Dm755 -t /usr/local/bin/ pihole install -Dm644 ./advanced/bash-completion/pihole /etc/bash_completion.d/pihole - echo " done." + echo " done." else echo " *** ERROR: Local repo ${core_repo} not found, exiting." exit 1 @@ -671,258 +668,258 @@ installScripts() { } installConfigs() { - # Install the configs from /etc/.pihole to their various locations - echo ":::" - echo "::: Installing configs..." - version_check_dnsmasq - if [ ! -d "/etc/lighttpd" ]; then - mkdir /etc/lighttpd - chown "${USER}":root /etc/lighttpd - elif [ -f "/etc/lighttpd/lighttpd.conf" ]; then - mv /etc/lighttpd/lighttpd.conf /etc/lighttpd/lighttpd.conf.orig - fi - cp /etc/.pihole/advanced/${LIGHTTPD_CFG} /etc/lighttpd/lighttpd.conf - mkdir -p /var/run/lighttpd - chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/run/lighttpd - mkdir -p /var/cache/lighttpd/compress - chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/compress - mkdir -p /var/cache/lighttpd/uploads - chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/uploads + # Install the configs from /etc/.pihole to their various locations + echo ":::" + echo "::: Installing configs..." + version_check_dnsmasq + if [ ! -d "/etc/lighttpd" ]; then + mkdir /etc/lighttpd + chown "${USER}":root /etc/lighttpd + elif [ -f "/etc/lighttpd/lighttpd.conf" ]; then + mv /etc/lighttpd/lighttpd.conf /etc/lighttpd/lighttpd.conf.orig + fi + cp /etc/.pihole/advanced/${LIGHTTPD_CFG} /etc/lighttpd/lighttpd.conf + mkdir -p /var/run/lighttpd + chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/run/lighttpd + mkdir -p /var/cache/lighttpd/compress + chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/compress + mkdir -p /var/cache/lighttpd/uploads + chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/uploads } stop_service() { - # Stop service passed in as argument. - # Can softfail, as process may not be installed when this is called - echo ":::" - echo -n "::: Stopping ${1} service..." - if [ -x "$(command -v systemctl)" ]; then - systemctl stop "${1}" &> /dev/null & spinner $! || true - else - service "${1}" stop &> /dev/null & spinner $! || true - fi - echo " done." + # Stop service passed in as argument. + # Can softfail, as process may not be installed when this is called + echo ":::" + echo -n "::: Stopping ${1} service..." + if [ -x "$(command -v systemctl)" ]; then + systemctl stop "${1}" &> /dev/null & spinner $! || true + else + service "${1}" stop &> /dev/null & spinner $! || true + fi + echo " done." } start_service() { - # Start/Restart service passed in as argument - # This should not fail, it's an error if it does - echo ":::" - echo -n "::: Starting ${1} service..." - if [ -x "$(command -v systemctl)" ]; then - systemctl restart "${1}" &> /dev/null & spinner $! - else - service "${1}" restart &> /dev/null & spinner $! - fi - echo " done." + # Start/Restart service passed in as argument + # This should not fail, it's an error if it does + echo ":::" + echo -n "::: Starting ${1} service..." + if [ -x "$(command -v systemctl)" ]; then + systemctl restart "${1}" &> /dev/null & spinner $! + else + service "${1}" restart &> /dev/null & spinner $! + fi + echo " done." } enable_service() { - # Enable service so that it will start with next reboot - echo ":::" - echo -n "::: Enabling ${1} service to start on reboot..." - if [ -x "$(command -v systemctl)" ]; then - systemctl enable "${1}" &> /dev/null & spinner $! - else - update-rc.d "${1}" defaults &> /dev/null & spinner $! - fi - echo " done." + # Enable service so that it will start with next reboot + echo ":::" + echo -n "::: Enabling ${1} service to start on reboot..." + if [ -x "$(command -v systemctl)" ]; then + systemctl enable "${1}" &> /dev/null & spinner $! + else + update-rc.d "${1}" defaults &> /dev/null & spinner $! + fi + echo " done." } update_pacakge_cache() { - #Running apt-get update/upgrade with minimal output can cause some issues with - #requiring user input (e.g password for phpmyadmin see #218) + #Running apt-get update/upgrade with minimal output can cause some issues with + #requiring user input (e.g password for phpmyadmin see #218) - #Check to see if apt-get update has already been run today - #it needs to have been run at least once on new installs! - timestamp=$(stat -c %Y ${PKG_CACHE}) - timestampAsDate=$(date -d @"${timestamp}" "+%b %e") - today=$(date "+%b %e") + #Check to see if apt-get update has already been run today + #it needs to have been run at least once on new installs! + timestamp=$(stat -c %Y ${PKG_CACHE}) + timestampAsDate=$(date -d @"${timestamp}" "+%b %e") + today=$(date "+%b %e") - if [ ! "${today}" == "${timestampAsDate}" ]; then - #update package lists - echo ":::" - echo -n "::: ${PKG_MANAGER} update has not been run today. Running now..." - ${UPDATE_PKG_CACHE} &> /dev/null & spinner $! - echo " done!" - fi + if [ ! "${today}" == "${timestampAsDate}" ]; then + #update package lists + echo ":::" + echo -n "::: ${PKG_MANAGER} update has not been run today. Running now..." + ${UPDATE_PKG_CACHE} &> /dev/null & spinner $! + echo " done!" + fi } notify_package_updates_available() { # Let user know if they have outdated packages on their system and # advise them to run a package update at soonest possible. - echo ":::" - echo -n "::: Checking ${PKG_MANAGER} for upgraded packages...." - updatesToInstall=$(eval "${PKG_COUNT}") - echo " done!" - echo ":::" - if [[ ${updatesToInstall} -eq "0" ]]; then - echo "::: Your system is up to date! Continuing with Pi-hole installation..." - else - echo "::: There are ${updatesToInstall} updates available for your system!" - echo "::: We recommend you run '${PKG_UPDATE}' after installing Pi-Hole! " - echo ":::" - fi + echo ":::" + echo -n "::: Checking ${PKG_MANAGER} for upgraded packages...." + updatesToInstall=$(eval "${PKG_COUNT}") + echo " done!" + echo ":::" + if [[ ${updatesToInstall} -eq "0" ]]; then + echo "::: Your system is up to date! Continuing with Pi-hole installation..." + else + echo "::: There are ${updatesToInstall} updates available for your system!" + echo "::: We recommend you run '${PKG_UPDATE}' after installing Pi-Hole! " + echo ":::" + fi } install_dependent_packages() { - # Install packages passed in via argument array - # No spinner - conflicts with set -e - declare -a argArray1=("${!1}") + # Install packages passed in via argument array + # No spinner - conflicts with set -e + declare -a argArray1=("${!1}") - for i in "${argArray1[@]}"; do - echo -n "::: Checking for $i..." - package_check_install "${i}" &> /dev/null - echo " installed!" - done + for i in "${argArray1[@]}"; do + echo -n "::: Checking for $i..." + package_check_install "${i}" &> /dev/null + echo " installed!" + done } CreateLogFile() { - # Create logfiles if necessary - echo ":::" - echo -n "::: Creating log file and changing owner to dnsmasq..." - if [ ! -f /var/log/pihole.log ]; then - touch /var/log/pihole.log - chmod 644 /var/log/pihole.log - chown "${DNSMASQ_USER}":root /var/log/pihole.log - echo " done!" - else - echo " already exists!" - fi + # Create logfiles if necessary + echo ":::" + echo -n "::: Creating log file and changing owner to dnsmasq..." + if [ ! -f /var/log/pihole.log ]; then + touch /var/log/pihole.log + chmod 644 /var/log/pihole.log + chown "${DNSMASQ_USER}":root /var/log/pihole.log + echo " done!" + else + echo " already exists!" + fi } installPiholeWeb() { - # Install the web interface - echo ":::" - echo "::: Installing pihole custom index page..." - if [ -d "/var/www/html/pihole" ]; then - if [ -f "/var/www/html/pihole/index.html" ]; then - echo "::: Existing index.html detected, not overwriting" - else - echo -n "::: index.html missing, replacing... " - cp /etc/.pihole/advanced/index.html /var/www/html/pihole/ - echo " done!" - fi + # Install the web interface + echo ":::" + echo "::: Installing pihole custom index page..." + if [ -d "/var/www/html/pihole" ]; then + if [ -f "/var/www/html/pihole/index.html" ]; then + echo "::: Existing index.html detected, not overwriting" + else + echo -n "::: index.html missing, replacing... " + cp /etc/.pihole/advanced/index.html /var/www/html/pihole/ + echo " done!" + fi - if [ -f "/var/www/html/pihole/index.js" ]; then - echo "::: Existing index.js detected, not overwriting" - else - echo -n "::: index.js missing, replacing... " - cp /etc/.pihole/advanced/index.js /var/www/html/pihole/ - echo " done!" - fi + if [ -f "/var/www/html/pihole/index.js" ]; then + echo "::: Existing index.js detected, not overwriting" + else + echo -n "::: index.js missing, replacing... " + cp /etc/.pihole/advanced/index.js /var/www/html/pihole/ + echo " done!" + fi - else - mkdir /var/www/html/pihole - if [ -f /var/www/html/index.lighttpd.html ]; then - mv /var/www/html/index.lighttpd.html /var/www/html/index.lighttpd.orig - else - printf "\n:::\tNo default index.lighttpd.html file found... not backing up" - fi - cp /etc/.pihole/advanced/index.* /var/www/html/pihole/. - echo " done!" - fi - # Install Sudoer file - echo ":::" - echo -n "::: Installing sudoer file..." - mkdir -p /etc/sudoers.d/ - cp /etc/.pihole/advanced/pihole.sudo /etc/sudoers.d/pihole - chmod 0440 /etc/sudoers.d/pihole - echo " done!" + else + mkdir /var/www/html/pihole + if [ -f /var/www/html/index.lighttpd.html ]; then + mv /var/www/html/index.lighttpd.html /var/www/html/index.lighttpd.orig + else + printf "\n:::\tNo default index.lighttpd.html file found... not backing up" + fi + cp /etc/.pihole/advanced/index.* /var/www/html/pihole/. + echo " done!" + fi + # Install Sudoer file + echo ":::" + echo -n "::: Installing sudoer file..." + mkdir -p /etc/sudoers.d/ + cp /etc/.pihole/advanced/pihole.sudo /etc/sudoers.d/pihole + chmod 0440 /etc/sudoers.d/pihole + echo " done!" } installCron() { - # Install the cron job - echo ":::" - echo -n "::: Installing latest Cron script..." - cp /etc/.pihole/advanced/pihole.cron /etc/cron.d/pihole - echo " done!" + # Install the cron job + echo ":::" + echo -n "::: Installing latest Cron script..." + cp /etc/.pihole/advanced/pihole.cron /etc/cron.d/pihole + echo " done!" } runGravity() { - # Run gravity.sh to build blacklists - echo ":::" - echo "::: Preparing to run gravity.sh to refresh hosts..." - if ls /etc/pihole/list* 1> /dev/null 2>&1; then - echo "::: Cleaning up previous install (preserving whitelist/blacklist)" - rm /etc/pihole/list.* - fi - echo "::: Running gravity.sh" - /opt/pihole/gravity.sh + # Run gravity.sh to build blacklists + echo ":::" + echo "::: Preparing to run gravity.sh to refresh hosts..." + if ls /etc/pihole/list* 1> /dev/null 2>&1; then + echo "::: Cleaning up previous install (preserving whitelist/blacklist)" + rm /etc/pihole/list.* + fi + echo "::: Running gravity.sh" + /opt/pihole/gravity.sh } create_pihole_user() { - # Check if user pihole exists and create if not - echo "::: Checking if user 'pihole' exists..." - id -u pihole &> /dev/null && echo "::: User 'pihole' already exists" || (echo "::: User 'pihole' doesn't exist. Creating..." && useradd -r -s /usr/sbin/nologin pihole) + # Check if user pihole exists and create if not + echo "::: Checking if user 'pihole' exists..." + id -u pihole &> /dev/null && echo "::: User 'pihole' already exists" || (echo "::: User 'pihole' doesn't exist. Creating..." && useradd -r -s /usr/sbin/nologin pihole) } configureFirewall() { - # Allow HTTP and DNS traffic - if [ -x "$(command -v firewall-cmd)" ]; then - firewall-cmd --state &> /dev/null && ( echo "::: Configuring firewalld for httpd and dnsmasq.." && firewall-cmd --permanent --add-port=80/tcp && firewall-cmd --permanent --add-port=53/tcp \ - && firewall-cmd --permanent --add-port=53/udp && firewall-cmd --reload) || echo "::: FirewallD not enabled" - elif [ -x "$(command -v iptables)" ]; then - echo "::: Configuring iptables for httpd and dnsmasq.." - iptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT - iptables -A INPUT -p tcp -m tcp --dport 53 -j ACCEPT - iptables -A INPUT -p udp -m udp --dport 53 -j ACCEPT - else - echo "::: No firewall detected.. skipping firewall configuration." - fi + # Allow HTTP and DNS traffic + if [ -x "$(command -v firewall-cmd)" ]; then + firewall-cmd --state &> /dev/null && ( echo "::: Configuring firewalld for httpd and dnsmasq.." && firewall-cmd --permanent --add-port=80/tcp && firewall-cmd --permanent --add-port=53/tcp \ + && firewall-cmd --permanent --add-port=53/udp && firewall-cmd --reload) || echo "::: FirewallD not enabled" + elif [ -x "$(command -v iptables)" ]; then + echo "::: Configuring iptables for httpd and dnsmasq.." + iptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT + iptables -A INPUT -p tcp -m tcp --dport 53 -j ACCEPT + iptables -A INPUT -p udp -m udp --dport 53 -j ACCEPT + else + echo "::: No firewall detected.. skipping firewall configuration." + fi } finalExports() { - # Update variables in setupVars.conf file - if [ -e "${setupVars}" ]; then - sed -i.update.bak '/PIHOLE_INTERFACE/d;/IPV4_ADDRESS/d;/IPV6_ADDRESS/d;/PIHOLE_DNS_1/d;/PIHOLE_DNS_2/d;/QUERY_LOGGING/d;' "${setupVars}" - fi + # Update variables in setupVars.conf file + if [ -e "${setupVars}" ]; then + sed -i.update.bak '/PIHOLE_INTERFACE/d;/IPV4_ADDRESS/d;/IPV6_ADDRESS/d;/PIHOLE_DNS_1/d;/PIHOLE_DNS_2/d;/QUERY_LOGGING/d;' "${setupVars}" + fi { - echo "PIHOLE_INTERFACE=${PIHOLE_INTERFACE}" - echo "IPV4_ADDRESS=${IPV4_ADDRESS}" - echo "IPV6_ADDRESS=${IPV6_ADDRESS}" - echo "PIHOLE_DNS_1=${PIHOLE_DNS_1}" - echo "PIHOLE_DNS_2=${PIHOLE_DNS_2}" - echo "QUERY_LOGGING=${QUERY_LOGGING}" + echo "PIHOLE_INTERFACE=${PIHOLE_INTERFACE}" + echo "IPV4_ADDRESS=${IPV4_ADDRESS}" + echo "IPV6_ADDRESS=${IPV6_ADDRESS}" + echo "PIHOLE_DNS_1=${PIHOLE_DNS_1}" + echo "PIHOLE_DNS_2=${PIHOLE_DNS_2}" + echo "QUERY_LOGGING=${QUERY_LOGGING}" }>> "${setupVars}" } installPihole() { - # Install base files and web interface - create_pihole_user - if [ ! -d "/var/www/html" ]; then - mkdir -p /var/www/html - fi - chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/www/html - chmod 775 /var/www/html - usermod -a -G ${LIGHTTPD_GROUP} pihole - if [ -x "$(command -v lighty-enable-mod)" ]; then - lighty-enable-mod fastcgi fastcgi-php > /dev/null || true - else - printf "\n:::\tWarning: 'lighty-enable-mod' utility not found. Please ensure fastcgi is enabled if you experience issues.\n" - fi - installScripts - installConfigs - CreateLogFile - configureSelinux - installPiholeWeb - installCron - configureFirewall - finalExports - runGravity + # Install base files and web interface + create_pihole_user + if [ ! -d "/var/www/html" ]; then + mkdir -p /var/www/html + fi + chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/www/html + chmod 775 /var/www/html + usermod -a -G ${LIGHTTPD_GROUP} pihole + if [ -x "$(command -v lighty-enable-mod)" ]; then + lighty-enable-mod fastcgi fastcgi-php > /dev/null || true + else + printf "\n:::\tWarning: 'lighty-enable-mod' utility not found. Please ensure fastcgi is enabled if you experience issues.\n" + fi + installScripts + installConfigs + CreateLogFile + configureSelinux + installPiholeWeb + installCron + configureFirewall + finalExports + runGravity } accountForRefactor() { - # At some point in the future this list can be pruned, for now we'll need it to ensure updates don't break. + # At some point in the future this list can be pruned, for now we'll need it to ensure updates don't break. - # Refactoring of install script has changed the name of a couple of variables. Sort them out here. + # Refactoring of install script has changed the name of a couple of variables. Sort them out here. - sed -i 's/piholeInterface/PIHOLE_INTERFACE/g' ${setupVars} - sed -i 's/IPv4_address/IPV4_ADDRESS/g' ${setupVars} - sed -i 's/IPv4addr/IPV4_ADDRESS/g' ${setupVars} - sed -i 's/IPv6_address/IPV6_ADDRESS/g' ${setupVars} - sed -i 's/piholeIPv6/IPV6_ADDRESS/g' ${setupVars} - sed -i 's/piholeDNS1/PIHOLE_DNS_1/g' ${setupVars} - sed -i 's/piholeDNS2/PIHOLE_DNS_2/g' ${setupVars} + sed -i 's/piholeInterface/PIHOLE_INTERFACE/g' ${setupVars} + sed -i 's/IPv4_address/IPV4_ADDRESS/g' ${setupVars} + sed -i 's/IPv4addr/IPV4_ADDRESS/g' ${setupVars} + sed -i 's/IPv6_address/IPV6_ADDRESS/g' ${setupVars} + sed -i 's/piholeIPv6/IPV6_ADDRESS/g' ${setupVars} + sed -i 's/piholeDNS1/PIHOLE_DNS_1/g' ${setupVars} + sed -i 's/piholeDNS2/PIHOLE_DNS_2/g' ${setupVars} } @@ -943,29 +940,29 @@ updatePihole() { } configureSelinux() { - if [ -x "$(command -v getenforce)" ]; then - printf "\n::: SELinux Detected\n" - printf ":::\tChecking for SELinux policy development packages..." - package_check_install "selinux-policy-devel" > /dev/null - echo " installed!" - printf ":::\tEnabling httpd server side includes (SSI).. " - setsebool -P httpd_ssi_exec on &> /dev/null && echo "Success" || echo "SELinux not enabled" - printf "\n:::\tCompiling Pi-Hole SELinux policy..\n" - if ! [ -x "$(command -v systemctl)" ]; then - sed -i.bak '/systemd/d' /etc/.pihole/advanced/selinux/pihole.te - fi - checkmodule -M -m -o /etc/pihole/pihole.mod /etc/.pihole/advanced/selinux/pihole.te - semodule_package -o /etc/pihole/pihole.pp -m /etc/pihole/pihole.mod - semodule -i /etc/pihole/pihole.pp - rm -f /etc/pihole/pihole.mod - semodule -l | grep pihole &> /dev/null && echo "::: Installed Pi-Hole SELinux policy" || echo "::: Warning: Pi-Hole SELinux policy did not install." - fi + if [ -x "$(command -v getenforce)" ]; then + printf "\n::: SELinux Detected\n" + printf ":::\tChecking for SELinux policy development packages..." + package_check_install "selinux-policy-devel" > /dev/null + echo " installed!" + printf ":::\tEnabling httpd server side includes (SSI).. " + setsebool -P httpd_ssi_exec on &> /dev/null && echo "Success" || echo "SELinux not enabled" + printf "\n:::\tCompiling Pi-Hole SELinux policy..\n" + if ! [ -x "$(command -v systemctl)" ]; then + sed -i.bak '/systemd/d' /etc/.pihole/advanced/selinux/pihole.te + fi + checkmodule -M -m -o /etc/pihole/pihole.mod /etc/.pihole/advanced/selinux/pihole.te + semodule_package -o /etc/pihole/pihole.pp -m /etc/pihole/pihole.mod + semodule -i /etc/pihole/pihole.pp + rm -f /etc/pihole/pihole.mod + semodule -l | grep pihole &> /dev/null && echo "::: Installed Pi-Hole SELinux policy" || echo "::: Warning: Pi-Hole SELinux policy did not install." + fi } displayFinalMessage() { - if (( ${#1} > 0 )) ; then - # Final completion message to user - whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: + if (( ${#1} > 0 )) ; then + # Final completion message to user + whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: IPv4: ${IPV4_ADDRESS%/*} IPv6: ${IPV6_ADDRESS} @@ -975,8 +972,8 @@ If you set a new IP address, you should restart the Pi. The install log is in /etc/pihole. View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin The currently set password is ${1}" ${r} ${c} - else - whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: + else + whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: IPv4: ${IPV4_ADDRESS%/*} IPv6: ${IPV6_ADDRESS} @@ -985,170 +982,169 @@ If you set a new IP address, you should restart the Pi. The install log is in /etc/pihole. View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin" ${r} ${c} - fi + fi } update_dialogs() { - # reconfigure - if [ "${reconfigure}" = true ]; then - opt1a="Repair" - opt1b="This will retain existing settings" - strAdd="You will remain on the same version" - else - opt1a="Update" - opt1b="This will retain existing settings." - strAdd="You will be updated to the latest version." - fi - opt2a="Reconfigure" - opt2b="This will allow you to enter new settings" + # reconfigure + if [ "${reconfigure}" = true ]; then + opt1a="Repair" + opt1b="This will retain existing settings" + strAdd="You will remain on the same version" + else + opt1a="Update" + opt1b="This will retain existing settings." + strAdd="You will be updated to the latest version." + fi + opt2a="Reconfigure" + opt2b="This will allow you to enter new settings" - UpdateCmd=$(whiptail --title "Existing Install Detected!" --menu "\n\nWe have detected an existing install.\n\nPlease choose from the following options: \n($strAdd)" ${r} ${c} 2 \ - "${opt1a}" "${opt1b}" \ - "${opt2a}" "${opt2b}" 3>&2 2>&1 1>&3) - - if [[ $? = 0 ]];then - case ${UpdateCmd} in - ${opt1a}) - echo "::: ${opt1a} option selected." - useUpdateVars=true - ;; - ${opt2a}) - echo "::: ${opt2a} option selected" - useUpdateVars=false - ;; - esac - else - echo "::: Cancel selected. Exiting..." - exit 1 - fi + UpdateCmd=$(whiptail --title "Existing Install Detected!" --menu "\n\nWe have detected an existing install.\n\nPlease choose from the following options: \n($strAdd)" ${r} ${c} 2 \ + "${opt1a}" "${opt1b}" \ + "${opt2a}" "${opt2b}" 3>&2 2>&1 1>&3) + if [[ $? = 0 ]];then + case ${UpdateCmd} in + ${opt1a}) + echo "::: ${opt1a} option selected." + useUpdateVars=true + ;; + ${opt2a}) + echo "::: ${opt2a} option selected" + useUpdateVars=false + ;; + esac + else + echo "::: Cancel selected. Exiting..." + exit 1 + fi } main() { # Check arguments for the undocumented flags - for var in "$@"; do - case "$var" in - "--reconfigure" ) reconfigure=true;; - "--i_do_not_follow_recommendations" ) skipSpaceCheck=false;; - "--unattended" ) runUnattended=true;; - esac - done + for var in "$@"; do + case "$var" in + "--reconfigure" ) reconfigure=true;; + "--i_do_not_follow_recommendations" ) skipSpaceCheck=false;; + "--unattended" ) runUnattended=true;; + esac + done - if [[ -f ${setupVars} ]]; then - if [[ "${runUnattended}" == true ]]; then - echo "::: --unattended passed to install script, no whiptail dialogs will be displayed" - useUpdateVars=true - else - update_dialogs - fi - fi + if [[ -f ${setupVars} ]]; then + if [[ "${runUnattended}" == true ]]; then + echo "::: --unattended passed to install script, no whiptail dialogs will be displayed" + useUpdateVars=true + else + update_dialogs + fi + fi - # Start the installer - # Verify there is enough disk space for the install - if [[ "${skipSpaceCheck}" == true ]]; then - echo "::: --i_do_not_follow_recommendations passed to script, skipping free disk space verification!" - else - verifyFreeDiskSpace - fi + # Start the installer + # Verify there is enough disk space for the install + if [[ "${skipSpaceCheck}" == true ]]; then + echo "::: --i_do_not_follow_recommendations passed to script, skipping free disk space verification!" + else + verifyFreeDiskSpace + fi - # Update package cache - update_pacakge_cache + # Update package cache + update_pacakge_cache - # Notify user of package availability - notify_package_updates_available + # Notify user of package availability + notify_package_updates_available - # Install packages used by this installation script - install_dependent_packages INSTALLER_DEPS[@] + # Install packages used by this installation script + install_dependent_packages INSTALLER_DEPS[@] - if [[ "${reconfigure}" == true ]]; then - echo "::: --reconfigure passed to install script. Not downloading/updating local repos" - else - # Get Git files for Core and Admin - getGitFiles ${PI_HOLE_LOCAL_REPO} ${piholeGitUrl} - getGitFiles ${webInterfaceDir} ${webInterfaceGitUrl} - fi + if [[ "${reconfigure}" == true ]]; then + echo "::: --reconfigure passed to install script. Not downloading/updating local repos" + else + # Get Git files for Core and Admin + getGitFiles ${PI_HOLE_LOCAL_REPO} ${piholeGitUrl} + getGitFiles ${webInterfaceDir} ${webInterfaceGitUrl} + fi - if [[ ${useUpdateVars} == false ]]; then - # Display welcome dialogs - welcomeDialogs - # Create directory for Pi-hole storage - mkdir -p /etc/pihole/ - # Remove legacy scripts from previous storage location - remove_legacy_scripts - # Stop resolver and webserver while installing proceses - stop_service dnsmasq - stop_service lighttpd - # Determine available interfaces - get_available_interfaces - # Find interfaces and let the user choose one - chooseInterface - # Let the user decide if they want to block ads over IPv4 and/or IPv6 - use4andor6 - # Decide what upstream DNS Servers to use - setDNS - # Let the user decide if they want query logging enabled... - setLogging + if [[ ${useUpdateVars} == false ]]; then + # Display welcome dialogs + welcomeDialogs + # Create directory for Pi-hole storage + mkdir -p /etc/pihole/ + # Remove legacy scripts from previous storage location + remove_legacy_scripts + # Stop resolver and webserver while installing proceses + stop_service dnsmasq + stop_service lighttpd + # Determine available interfaces + get_available_interfaces + # Find interfaces and let the user choose one + chooseInterface + # Let the user decide if they want to block ads over IPv4 and/or IPv6 + use4andor6 + # Decide what upstream DNS Servers to use + setDNS + # Let the user decide if they want query logging enabled... + setLogging - # Install packages used by the Pi-hole - install_dependent_packages PIHOLE_DEPS[@] + # Install packages used by the Pi-hole + install_dependent_packages PIHOLE_DEPS[@] - # Install and log everything to a file + # Install and log everything to a file installPihole | tee ${tmpLog} - else - # update packages used by the Pi-hole - install_dependent_packages PIHOLE_DEPS[@] + else + # update packages used by the Pi-hole + install_dependent_packages PIHOLE_DEPS[@] - updatePihole | tee ${tmpLog} - fi + updatePihole | tee ${tmpLog} + fi - # Move the log file into /etc/pihole for storage - mv ${tmpLog} ${instalLogLoc} + # Move the log file into /etc/pihole for storage + mv ${tmpLog} ${instalLogLoc} - # Add password to web UI if there is none - pw="" - if [[ $(grep 'WEBPASSWORD' -c /etc/pihole/setupVars.conf) == 0 ]] ; then - pw=$(tr -dc _A-Z-a-z-0-9 < /dev/urandom | head -c 8) - pihole -a -p ${pw} - fi + # Add password to web UI if there is none + pw="" + if [[ $(grep 'WEBPASSWORD' -c /etc/pihole/setupVars.conf) == 0 ]] ; then + pw=$(tr -dc _A-Z-a-z-0-9 < /dev/urandom | head -c 8) + pihole -a -p ${pw} + fi - if [[ "${useUpdateVars}" == false ]]; then - displayFinalMessage ${pw} - fi + if [[ "${useUpdateVars}" == false ]]; then + displayFinalMessage ${pw} + fi - echo "::: Restarting services..." - # Start services - start_service dnsmasq - enable_service dnsmasq - start_service lighttpd - enable_service lighttpd - echo "::: done." + echo "::: Restarting services..." + # Start services + start_service dnsmasq + enable_service dnsmasq + start_service lighttpd + enable_service lighttpd + echo "::: done." - echo ":::" - if [[ "${useUpdateVars}" == false ]]; then - echo "::: Installation Complete! Configure your devices to use the Pi-hole as their DNS server using:" - echo "::: ${IPV4_ADDRESS%/*}" - echo "::: ${IPV6_ADDRESS}" - echo ":::" - echo "::: If you set a new IP address, you should restart the Pi." + echo ":::" + if [[ "${useUpdateVars}" == false ]]; then + echo "::: Installation Complete! Configure your devices to use the Pi-hole as their DNS server using:" + echo "::: ${IPV4_ADDRESS%/*}" + echo "::: ${IPV6_ADDRESS}" + echo ":::" + echo "::: If you set a new IP address, you should restart the Pi." echo "::: View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin" - else - echo "::: Update complete!" - fi + else + echo "::: Update complete!" + fi - if (( ${#pw} > 0 )) ; then - echo ":::" - echo "::: Note: As security measure a password has been installed for your web interface" - echo "::: The currently set password is" - echo "::: ${pw}" - echo ":::" - echo "::: You can always change it using" - echo "::: pihole -a -p new_password" - fi + if (( ${#pw} > 0 )) ; then + echo ":::" + echo "::: Note: As security measure a password has been installed for your web interface" + echo "::: The currently set password is" + echo "::: ${pw}" + echo ":::" + echo "::: You can always change it using" + echo "::: pihole -a -p new_password" + fi - echo ":::" - echo "::: The install log is located at: /etc/pihole/install.log" + echo ":::" + echo "::: The install log is located at: /etc/pihole/install.log" } if [[ "${PH_TEST}" != true ]] ; then - main "$@" + main "$@" fi From 8117ec8e20fe7253009b87922cdbc9d9e50a5ddc Mon Sep 17 00:00:00 2001 From: Dan Schaper Date: Tue, 20 Dec 2016 17:50:44 -0800 Subject: [PATCH 31/89] Remove v1 legacy detection code. Signed-off-by: Dan Schaper --- automated install/basic-install.sh | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index a04cac3f..77d28651 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -62,7 +62,7 @@ else echo ":::" echo "::: Detecting the presence of the sudo utility for continuation of this install..." - if [ -x "$(command -v sudo)" ]; then + if command -v sudo; then echo "::: Utility sudo located." exec curl -sSL https://install.pi-hole.net | sudo bash "$@" exit $? @@ -74,7 +74,7 @@ fi # Compatibility -if [[ $(command -v apt-get) ]]; then +if command -v apt-get; then #Debian Family ############################################# PKG_MANAGER="apt-get" @@ -101,9 +101,9 @@ if [[ $(command -v apt-get) ]]; then package_check_install() { dpkg-query -W -f='${Status}' "${1}" 2>/dev/null | grep -c "ok installed" || ${PKG_INSTALL} "${1}" } -elif [ $(command -v rpm) ]; then +elif command -v rpm; then # Fedora Family - if [ $(command -v dnf) ]; then + if command -v dnf; then PKG_MANAGER="dnf" else PKG_MANAGER="yum" @@ -623,16 +623,6 @@ version_check_dnsmasq() { fi } -remove_legacy_scripts() { - #Tidy up /usr/local/bin directory if installing over previous install. - oldFiles=( gravity chronometer whitelist blacklist piholeLogFlush updateDashboard uninstall setupLCD piholeDebug) - for i in "${oldFiles[@]}"; do - if [ -f "/usr/local/bin/$i.sh" ]; then - rm /usr/local/bin/"$i".sh - fi - done -} - clean_existing() { # Clean an exiting installation to prepare for upgrade/reinstall # ${1} Directory to clean; ${2} Array of files to remove From 58261098fbb2847eb7e08f4610c998e165b4e9f1 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Wed, 21 Dec 2016 12:07:44 +0100 Subject: [PATCH 32/89] We should not call a function that Dan deleted --- automated install/basic-install.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 0c47aacd..cddc4dbf 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -1059,8 +1059,6 @@ main() { welcomeDialogs # Create directory for Pi-hole storage mkdir -p /etc/pihole/ - # Remove legacy scripts from previous storage location - remove_legacy_scripts # Stop resolver and webserver while installing proceses stop_service dnsmasq stop_service lighttpd From 8841bdd2523be3774b245064173979c12d436df7 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Wed, 21 Dec 2016 12:57:02 +0100 Subject: [PATCH 33/89] Reset the if's --- automated install/basic-install.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index cddc4dbf..fb8570e0 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -62,7 +62,7 @@ else echo ":::" echo "::: Detecting the presence of the sudo utility for continuation of this install..." - if command -v sudo; then + if [ -x "$(command -v sudo)" ]; then echo "::: Utility sudo located." exec curl -sSL https://install.pi-hole.net | sudo bash "$@" exit $? @@ -74,7 +74,7 @@ fi # Compatibility -if command -v apt-get; then +if [[ $(command -v apt-get) ]]; then #Debian Family ############################################# PKG_MANAGER="apt-get" @@ -101,9 +101,9 @@ if command -v apt-get; then package_check_install() { dpkg-query -W -f='${Status}' "${1}" 2>/dev/null | grep -c "ok installed" || ${PKG_INSTALL} "${1}" } -elif command -v rpm; then +elif [ $(command -v rpm) ]; then # Fedora Family - if command -v dnf; then + if [ $(command -v dnf) ]; then PKG_MANAGER="dnf" else PKG_MANAGER="yum" From 4244f716e0c7b029f4fe086135bad39d22297b00 Mon Sep 17 00:00:00 2001 From: Dan Schaper Date: Wed, 21 Dec 2016 22:59:14 -0800 Subject: [PATCH 34/89] Create /opt/pihole before trying to install into it. Signed-off-by: Dan Schaper --- automated install/basic-install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 3dacdfff..b03b98d0 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -628,6 +628,7 @@ installScripts() { # Install files from local core repository if is_repo "${PI_HOLE_LOCAL_REPO}"; then cd "${PI_HOLE_LOCAL_REPO}" + install -o "${USER}" -Dm755 -d /opt/pihole install -o "${USER}" -Dm755 -t /opt/pihole/ gravity.sh install -o "${USER}" -Dm755 -t /opt/pihole/ ./advanced/Scripts/*.sh install -o "${USER}" -Dm755 -t /opt/pihole/ ./automated\ install/uninstall.sh From c9c28cb59afe904887a15412fe3a793466852aeb Mon Sep 17 00:00:00 2001 From: bcambl Date: Thu, 22 Dec 2016 03:33:32 -0600 Subject: [PATCH 35/89] replace 'git -C' with long version The -C argument was introduced in git 1.8.4. CentOS 7.3 at the time of this commit provides v1.8.3.1. see: https://git.kaarsemaker.net/git/commit/44e1e4d67d5148c245db362cc48c3cc6c2ec82ca/ Fixes #1004 --- automated install/basic-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 3dacdfff..9b19c19f 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -135,7 +135,7 @@ fi is_repo() { # Use git to check if directory is currently under VCS, return the value local directory="${1}" - git -C "${directory}" status --short &> /dev/null + (cd ${directory} && git status --short) &> /dev/null return } From cbf84c1840fecce74b8554058aa32e9b8b4dfd18 Mon Sep 17 00:00:00 2001 From: bcambl Date: Thu, 22 Dec 2016 04:32:42 -0600 Subject: [PATCH 36/89] replace subshell with command chain --- automated install/basic-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 9b19c19f..bb2de017 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -135,8 +135,8 @@ fi is_repo() { # Use git to check if directory is currently under VCS, return the value local directory="${1}" - (cd ${directory} && git status --short) &> /dev/null - return + curdir=$PWD; cd $directory; git status --short &> /dev/null; rc=$?; cd $curdir + return $rc } make_repo() { From 4626b8ced50d17a4cf2da0ee7a4b3bfcb14efa5d Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 22 Dec 2016 13:18:38 +0100 Subject: [PATCH 37/89] Removed fixed username from sudoers file --- advanced/pihole.sudo | 1 - 1 file changed, 1 deletion(-) diff --git a/advanced/pihole.sudo b/advanced/pihole.sudo index ef06249a..702bc7a5 100644 --- a/advanced/pihole.sudo +++ b/advanced/pihole.sudo @@ -9,4 +9,3 @@ # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -www-data ALL=NOPASSWD: /usr/local/bin/pihole From 60054da582b4c7d980944b768cd96ce924ac8356 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 22 Dec 2016 13:26:11 +0100 Subject: [PATCH 38/89] Add lighttpd user (OS dependent) to sudoers file --- automated install/basic-install.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index b03b98d0..e99a914a 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -801,6 +801,8 @@ installPiholeWeb() { echo -n "::: Installing sudoer file..." mkdir -p /etc/sudoers.d/ cp /etc/.pihole/advanced/pihole.sudo /etc/sudoers.d/pihole + # Add lighttpd user (OS dependent) to sudoers file + echo "${LIGHTTPD_USER} ALL=NOPASSWD: /usr/local/bin/pihole" >> /etc/sudoers.d/pihole chmod 0440 /etc/sudoers.d/pihole echo " done!" } From e88f58c34ecba8969e458fbcc0e80e1204f0dde5 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 22 Dec 2016 18:45:53 +0100 Subject: [PATCH 39/89] Remove grpping for today's log entries, because we flush the log every day --- advanced/Scripts/chronometer.sh | 4 +--- advanced/Scripts/update.sh | 0 2 files changed, 1 insertion(+), 3 deletions(-) mode change 100644 => 100755 advanced/Scripts/update.sh diff --git a/advanced/Scripts/chronometer.sh b/advanced/Scripts/chronometer.sh index c4b3d865..97777802 100755 --- a/advanced/Scripts/chronometer.sh +++ b/advanced/Scripts/chronometer.sh @@ -15,8 +15,6 @@ piLog="/var/log/pihole.log" gravity="/etc/pihole/gravity.list" -today=$(date "+%b %e") - . /etc/pihole/setupVars.conf CalcBlockedDomains() { @@ -35,7 +33,7 @@ CalcBlockedDomains() { CalcQueriesToday() { if [ -e "${piLog}" ]; then - queriesToday=$(cat "${piLog}" | grep "${today}" | awk '/query/ {print $6}' | wc -l) + queriesToday=$(cat "${piLog}" | awk '/query/ {print $6}' | wc -l) else queriesToday="Err." fi diff --git a/advanced/Scripts/update.sh b/advanced/Scripts/update.sh old mode 100644 new mode 100755 From 9b0390c9da5ee21f87b3e99ab9a099a8b294c0cb Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 22 Dec 2016 18:53:44 +0100 Subject: [PATCH 40/89] Space to Tab --- advanced/Scripts/chronometer.sh | 2 +- advanced/Scripts/update.sh | 0 2 files changed, 1 insertion(+), 1 deletion(-) mode change 100755 => 100644 advanced/Scripts/update.sh diff --git a/advanced/Scripts/chronometer.sh b/advanced/Scripts/chronometer.sh index 97777802..a2482d0d 100755 --- a/advanced/Scripts/chronometer.sh +++ b/advanced/Scripts/chronometer.sh @@ -102,7 +102,7 @@ normalChrono() { echo "Blocking: ${blockedDomainsTotal}" echo "Queries: ${queriesToday}" #same total calculation as dashboard - echo "Pi-holed: ${blockedToday} (${percentBlockedToday}%)" + echo "Pi-holed: ${blockedToday} (${percentBlockedToday}%)" sleep 5 done diff --git a/advanced/Scripts/update.sh b/advanced/Scripts/update.sh old mode 100755 new mode 100644 From bcf8139708c529db7c2b76e88af7e5bcf1b4b42e Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 22 Dec 2016 18:55:14 +0100 Subject: [PATCH 41/89] Removed cat where we don't need it --- advanced/Scripts/chronometer.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/advanced/Scripts/chronometer.sh b/advanced/Scripts/chronometer.sh index a2482d0d..702ed1c9 100755 --- a/advanced/Scripts/chronometer.sh +++ b/advanced/Scripts/chronometer.sh @@ -33,7 +33,7 @@ CalcBlockedDomains() { CalcQueriesToday() { if [ -e "${piLog}" ]; then - queriesToday=$(cat "${piLog}" | awk '/query/ {print $6}' | wc -l) + queriesToday=$(awk '/query/ {print $6}' < "${piLog}" | wc -l) else queriesToday="Err." fi @@ -41,7 +41,7 @@ CalcQueriesToday() { CalcblockedToday() { if [ -e "${piLog}" ] && [ -e "${gravity}" ];then - blockedToday=$(cat ${piLog} | awk '/\/etc\/pihole\/gravity.list/ && !/address/ {print $6}' | wc -l) + blockedToday=$(awk '/\/etc\/pihole\/gravity.list/ && !/address/ {print $6}' < "${piLog}" | wc -l) else blockedToday="Err." fi From a4f58b0a22ff9e0afdaa08db34c771f3f6d98a4c Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 22 Dec 2016 19:09:37 +0100 Subject: [PATCH 42/89] Look for "query[" instead of "query" to avoid false-positives, like listed below root@raspberrypi:/etc/.pihole# grep 'query' /var/log/pihole.log | grep -v 'query\[' Dec 22 16:29:50 dnsmasq[30801]: forwarded code.jquery.com to 208.67.222.222 Dec 22 16:29:50 dnsmasq[30801]: reply code.jquery.com is Dec 22 16:29:50 dnsmasq[30801]: reply code.jquery.netdna-cdn.com is 94.31.29.54 Dec 22 16:29:50 dnsmasq[30801]: reply code.jquery.netdna-cdn.com is 23.111.11.3 Dec 22 16:29:50 dnsmasq[30801]: reply code.jquery.netdna-cdn.com is 198.232.125.113 --- advanced/Scripts/chronometer.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/advanced/Scripts/chronometer.sh b/advanced/Scripts/chronometer.sh index 702ed1c9..a28bb868 100755 --- a/advanced/Scripts/chronometer.sh +++ b/advanced/Scripts/chronometer.sh @@ -33,7 +33,7 @@ CalcBlockedDomains() { CalcQueriesToday() { if [ -e "${piLog}" ]; then - queriesToday=$(awk '/query/ {print $6}' < "${piLog}" | wc -l) + queriesToday=$(awk '/query\[/ {print $6}' < "${piLog}" | wc -l) else queriesToday="Err." fi From bc8cf1b2d8d2a4edd8500e57ef597e3c9dec3a94 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 22 Dec 2016 19:23:06 +0100 Subject: [PATCH 43/89] Fixed wrong indentation --- automated install/basic-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index ab2bc464..14bf10f7 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -799,7 +799,7 @@ installPiholeWeb() { mkdir /var/www/html/pihole if [ -f /var/www/html/index.lighttpd.html ]; then mv /var/www/html/index.lighttpd.html /var/www/html/index.lighttpd.orig - else + else printf "\n:::\tNo default index.lighttpd.html file found... not backing up" fi cp /etc/.pihole/advanced/index.* /var/www/html/pihole/. From ea5ee7b0f96e1a0a68af1cd42160c04ab8c7ce78 Mon Sep 17 00:00:00 2001 From: Dan Schaper Date: Thu, 22 Dec 2016 11:30:36 -0800 Subject: [PATCH 44/89] Execute gravity in same shell as installer, don't subshell it. Signed-off-by: Dan Schaper --- automated install/basic-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 82ea64fd..33b1842d 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -822,7 +822,7 @@ runGravity() { rm /etc/pihole/list.* fi echo "::: Running gravity.sh" - /opt/pihole/gravity.sh + { /opt/pihole/gravity.sh; } } create_pihole_user() { From 513bc32d879ca8f106f60fc09db7a24d85c5c31d Mon Sep 17 00:00:00 2001 From: bcambl Date: Thu, 22 Dec 2016 19:57:13 -0600 Subject: [PATCH 45/89] is_repo: ensure directory exists --- automated install/basic-install.sh | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index b5ba93ba..48e98f14 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -135,8 +135,14 @@ fi is_repo() { # Use git to check if directory is currently under VCS, return the value local directory="${1}" - curdir=$PWD; cd $directory; git status --short &> /dev/null; rc=$?; cd $curdir - return $rc + if [ -d $directory ]; then + # git -C is not used here to support git versions older than 1.8.4 + curdir=$PWD; cd $directory; git status --short &> /dev/null; rc=$?; cd $curdir + return $rc + else + # non-zero return code if directory does not exist OR is not a valid git repository + return 1 + fi } make_repo() { From 5e53f484bedc4609c468bd61668671c17fd7a866 Mon Sep 17 00:00:00 2001 From: Adam Hill Date: Thu, 22 Dec 2016 22:00:17 -0600 Subject: [PATCH 46/89] Update pihole.cron Stop repeating the path. --- advanced/pihole.cron | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/advanced/pihole.cron b/advanced/pihole.cron index 8311acfb..cb9965f0 100644 --- a/advanced/pihole.cron +++ b/advanced/pihole.cron @@ -13,14 +13,15 @@ # scripts, any changes made to this file will be overwritten when the softare # is updated or re-installed. Please make any changes to the appropriate crontab # or other cron file snippets. +PATH="$PATH:/usr/local/bin/" # Pi-hole: Update the ad sources once a week on Sunday at 01:59 # Download any updates from the adlists -59 1 * * 7 root PATH="$PATH:/usr/local/bin/" pihole updateGravity +59 1 * * 7 root pihole updateGravity # Pi-hole: Update Pi-hole! Uncomment to enable auto update -#30 2 * * 7 root PATH="$PATH:/usr/local/bin/" pihole updatePihole +#30 2 * * 7 root pihole updatePihole # Pi-hole: Flush the log daily at 00:00 so it doesn't get out of control # Stats will be viewable in the Web interface thanks to the cron job above -00 00 * * * root PATH="$PATH:/usr/local/bin/" pihole flush +00 00 * * * root pihole flush From 1e7e3259b511a8d8a228906483ded06d2d28c83b Mon Sep 17 00:00:00 2001 From: bcambl Date: Thu, 22 Dec 2016 22:34:38 -0600 Subject: [PATCH 47/89] choose dns servers prior to interface configuration upstream DNS servers must be chosen prior interface configuration. Fixes #1019 --- automated install/basic-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index b5ba93ba..5bab5531 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -1056,10 +1056,10 @@ main() { get_available_interfaces # Find interfaces and let the user choose one chooseInterface - # Let the user decide if they want to block ads over IPv4 and/or IPv6 - use4andor6 # Decide what upstream DNS Servers to use setDNS + # Let the user decide if they want to block ads over IPv4 and/or IPv6 + use4andor6 # Let the user decide if they want query logging enabled... setLogging From 6b7b0e0eb3d8cca341f2ea48f5c84a46257403e5 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Fri, 23 Dec 2016 16:00:48 +0100 Subject: [PATCH 48/89] Test if /etc/pihole/adlists.default exists --- automated install/basic-install.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 48e98f14..0f7f0e7c 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -829,6 +829,10 @@ runGravity() { echo "::: Cleaning up previous install (preserving whitelist/blacklist)" rm /etc/pihole/list.* fi + # Test if /etc/pihole/adlists.default exists + if [[ ! -e /etc/pihole/adlists.default ]]; then + cp /etc/.pihole/adlists.default /etc/pihole/adlists.default + fi echo "::: Running gravity.sh" { /opt/pihole/gravity.sh; } } From afabf30ec6c663c80daeebe4954f7f2b7b4c982d Mon Sep 17 00:00:00 2001 From: DL6ER Date: Fri, 23 Dec 2016 16:57:51 +0100 Subject: [PATCH 49/89] Fix Fedora web UI support --- automated install/basic-install.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 7e4f62ef..bb1704dc 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -809,6 +809,13 @@ installPiholeWeb() { cp /etc/.pihole/advanced/pihole.sudo /etc/sudoers.d/pihole # Add lighttpd user (OS dependent) to sudoers file echo "${LIGHTTPD_USER} ALL=NOPASSWD: /usr/local/bin/pihole" >> /etc/sudoers.d/pihole + + if [[ LIGHTTPD_USER == "lighttpd" ]]; then + # Allow executing pihole via sudo with Fedora + # Usually /usr/local/bin is not permitted as directory for sudoable programms + echo "Defaults secure_path = /sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin" >> /etc/sudoers.d/pihole + fi + chmod 0440 /etc/sudoers.d/pihole echo " done!" } From c674a175eeeb94ec7c15706c8bc731ff3e8b8ed2 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Fri, 23 Dec 2016 17:02:56 +0100 Subject: [PATCH 50/89] Make grep case insensitive --- pihole | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pihole b/pihole index 5509df47..5a54fae4 100755 --- a/pihole +++ b/pihole @@ -72,9 +72,9 @@ scanList(){ list="${2}" method="${3}" if [[ ${method} == "-exact" ]] ; then - grep -E "(^|\s)${domain}($|\s)" "${list}" + grep -i -E "(^|\s)${domain}($|\s)" "${list}" else - grep "${domain}" "${list}" + grep -i "${domain}" "${list}" fi } From aa8e1497a315c5fdd59d7c7c465d62bd9ac9b87b Mon Sep 17 00:00:00 2001 From: DL6ER Date: Fri, 23 Dec 2016 17:12:38 +0100 Subject: [PATCH 51/89] Fixed error --- automated install/basic-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index bb1704dc..5156d733 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -810,7 +810,7 @@ installPiholeWeb() { # Add lighttpd user (OS dependent) to sudoers file echo "${LIGHTTPD_USER} ALL=NOPASSWD: /usr/local/bin/pihole" >> /etc/sudoers.d/pihole - if [[ LIGHTTPD_USER == "lighttpd" ]]; then + if [[ "$LIGHTTPD_USER" == "lighttpd" ]]; then # Allow executing pihole via sudo with Fedora # Usually /usr/local/bin is not permitted as directory for sudoable programms echo "Defaults secure_path = /sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin" >> /etc/sudoers.d/pihole From 2fb0dc0a4a34ac1c4bab8aed0d5c17417d024bef Mon Sep 17 00:00:00 2001 From: bcambl Date: Fri, 23 Dec 2016 10:27:52 -0600 Subject: [PATCH 52/89] add warning if SELinux is Enforcing --- advanced/selinux/pihole.te | 87 ------------------------------ automated install/basic-install.sh | 36 ++++++------- 2 files changed, 18 insertions(+), 105 deletions(-) delete mode 100644 advanced/selinux/pihole.te diff --git a/advanced/selinux/pihole.te b/advanced/selinux/pihole.te deleted file mode 100644 index 595755dd..00000000 --- a/advanced/selinux/pihole.te +++ /dev/null @@ -1,87 +0,0 @@ -module pihole 1.0; - -require { - type var_log_t; - type unconfined_t; - type init_t; - type auditd_t; - type syslogd_t; - type NetworkManager_t; - type mdadm_t; - type tuned_t; - type avahi_t; - type irqbalance_t; - type system_dbusd_t; - type kernel_t; - type httpd_sys_script_t; - type systemd_logind_t; - type httpd_t; - type policykit_t; - type dnsmasq_t; - type udev_t; - type postfix_pickup_t; - type sshd_t; - type crond_t; - type getty_t; - type lvm_t; - type postfix_qmgr_t; - type postfix_master_t; - class dir { getattr search }; - class file { read open setattr }; -} - -#============= dnsmasq_t ============== -allow dnsmasq_t var_log_t:file { open setattr }; - -#============= httpd_t ============== -allow httpd_t var_log_t:file { read open }; - -#============= httpd_sys_script_t (class: dir) ============== -allow httpd_sys_script_t NetworkManager_t:dir { getattr search }; -allow httpd_sys_script_t auditd_t:dir { getattr search }; -allow httpd_sys_script_t avahi_t:dir { getattr search }; -allow httpd_sys_script_t crond_t:dir { getattr search }; -allow httpd_sys_script_t dnsmasq_t:dir { getattr search }; -allow httpd_sys_script_t getty_t:dir { getattr search }; -allow httpd_sys_script_t httpd_t:dir { getattr search }; -allow httpd_sys_script_t init_t:dir { getattr search }; -allow httpd_sys_script_t irqbalance_t:dir { getattr search }; -allow httpd_sys_script_t kernel_t:dir { getattr search }; -allow httpd_sys_script_t lvm_t:dir { getattr search }; -allow httpd_sys_script_t mdadm_t:dir { getattr search }; -allow httpd_sys_script_t policykit_t:dir { getattr search }; -allow httpd_sys_script_t postfix_master_t:dir { getattr search }; -allow httpd_sys_script_t postfix_pickup_t:dir { getattr search }; -allow httpd_sys_script_t postfix_qmgr_t:dir { getattr search }; -allow httpd_sys_script_t sshd_t:dir { getattr search }; -allow httpd_sys_script_t syslogd_t:dir { getattr search }; -allow httpd_sys_script_t system_dbusd_t:dir { getattr search }; -allow httpd_sys_script_t systemd_logind_t:dir { getattr search }; -allow httpd_sys_script_t tuned_t:dir { getattr search }; -allow httpd_sys_script_t udev_t:dir { getattr search }; -allow httpd_sys_script_t unconfined_t:dir { getattr search }; - -#============= httpd_sys_script_t (class: file) ============== -allow httpd_sys_script_t NetworkManager_t:file { read open }; -allow httpd_sys_script_t auditd_t:file { read open }; -allow httpd_sys_script_t avahi_t:file { read open }; -allow httpd_sys_script_t crond_t:file { read open }; -allow httpd_sys_script_t dnsmasq_t:file { read open }; -allow httpd_sys_script_t getty_t:file { read open }; -allow httpd_sys_script_t httpd_t:file { read open }; -allow httpd_sys_script_t init_t:file { read open }; -allow httpd_sys_script_t irqbalance_t:file { read open }; -allow httpd_sys_script_t kernel_t:file { read open }; -allow httpd_sys_script_t lvm_t:file { read open }; -allow httpd_sys_script_t mdadm_t:file { read open }; -allow httpd_sys_script_t policykit_t:file { read open }; -allow httpd_sys_script_t postfix_master_t:file { read open }; -allow httpd_sys_script_t postfix_pickup_t:file { read open }; -allow httpd_sys_script_t postfix_qmgr_t:file { read open }; -allow httpd_sys_script_t sshd_t:file { read open }; -allow httpd_sys_script_t syslogd_t:file { read open }; -allow httpd_sys_script_t system_dbusd_t:file { read open }; -allow httpd_sys_script_t systemd_logind_t:file { read open }; -allow httpd_sys_script_t tuned_t:file { read open }; -allow httpd_sys_script_t udev_t:file { read open }; -allow httpd_sys_script_t unconfined_t:file { read open }; diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 7e4f62ef..0b32950d 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -890,7 +890,6 @@ installPihole() { installScripts installConfigs CreateLogFile - configureSelinux installPiholeWeb installCron configureFirewall @@ -921,7 +920,6 @@ updatePihole() { installScripts installConfigs CreateLogFile - configureSelinux installPiholeWeb installCron configureFirewall @@ -929,23 +927,22 @@ updatePihole() { runGravity } -configureSelinux() { +checkSelinux() { if [ -x "$(command -v getenforce)" ]; then - printf "\n::: SELinux Detected\n" - printf ":::\tChecking for SELinux policy development packages..." - package_check_install "selinux-policy-devel" > /dev/null - echo " installed!" - printf ":::\tEnabling httpd server side includes (SSI).. " - setsebool -P httpd_ssi_exec on &> /dev/null && echo "Success" || echo "SELinux not enabled" - printf "\n:::\tCompiling Pi-Hole SELinux policy..\n" - if ! [ -x "$(command -v systemctl)" ]; then - sed -i.bak '/systemd/d' /etc/.pihole/advanced/selinux/pihole.te + printf "\n::: SELinux Support Detected.." + getenforce | grep 'Enforcing' + if [ $? -eq 0 ]; then + printf "\n::: SELinux is being Enforced on your system" + printf "\n::: WARNING: PiHole does not support SELinux at this time.." + read -r -p "Continue with SELinux Enforcing? [y/N]" continue + if [[ $continue =~ ^([yY][eE][sS]|[yY])$ ]] + then + printf "\n::: Continuing installation with SELinux Enforcing.." + printf "\n::: Please refer to official SELinux documentation to create a custom policy." + else + exit 1 + fi fi - checkmodule -M -m -o /etc/pihole/pihole.mod /etc/.pihole/advanced/selinux/pihole.te - semodule_package -o /etc/pihole/pihole.pp -m /etc/pihole/pihole.mod - semodule -i /etc/pihole/pihole.pp - rm -f /etc/pihole/pihole.mod - semodule -l | grep pihole &> /dev/null && echo "::: Installed Pi-Hole SELinux policy" || echo "::: Warning: Pi-Hole SELinux policy did not install." fi } @@ -1011,7 +1008,10 @@ update_dialogs() { } main() { -# Check arguments for the undocumented flags + # Check if SELinux is Enforcing + checkSelinux + + # Check arguments for the undocumented flags for var in "$@"; do case "$var" in "--reconfigure" ) reconfigure=true;; From 73a80ff7dca240db484e42514ec14b3bc4e7398c Mon Sep 17 00:00:00 2001 From: Mcat12 Date: Fri, 23 Dec 2016 11:51:34 -0500 Subject: [PATCH 53/89] Align installer output for updating repo --- automated install/basic-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 5156d733..57f06518 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -158,7 +158,7 @@ make_repo() { update_repo() { local directory="${1}" # Pull the latest commits - echo -n "::: Updating repo in $1..." + echo -n "::: Updating repo in $1..." cd "${directory}" || exit 1 git stash -q &> /dev/null git pull -q &> /dev/null From fdb64a57022f8ac9ff5d98aaa0a7c64d6288bbb8 Mon Sep 17 00:00:00 2001 From: Promofaux Date: Fri, 23 Dec 2016 16:53:42 +0000 Subject: [PATCH 54/89] untested convert user prompt to whiptail --- automated install/basic-install.sh | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 15a0a868..d8899ce3 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -934,16 +934,14 @@ updatePihole() { runGravity } + + checkSelinux() { if [ -x "$(command -v getenforce)" ]; then printf "\n::: SELinux Support Detected.." getenforce | grep 'Enforcing' if [ $? -eq 0 ]; then - printf "\n::: SELinux is being Enforced on your system" - printf "\n::: WARNING: PiHole does not support SELinux at this time.." - read -r -p "Continue with SELinux Enforcing? [y/N]" continue - if [[ $continue =~ ^([yY][eE][sS]|[yY])$ ]] - then + if (whiptail --title "SELinux Detected" --yesno "SELinux is being Enforced on your system. \n Pi-hole does not support SELinux at this time...\nContinue with SELinux Enforcing?" 8 78); then printf "\n::: Continuing installation with SELinux Enforcing.." printf "\n::: Please refer to official SELinux documentation to create a custom policy." else From a8897becd2d753ed16f8d68628e38c08f9a165c0 Mon Sep 17 00:00:00 2001 From: Promofaux Date: Fri, 23 Dec 2016 16:55:56 +0000 Subject: [PATCH 55/89] Make sure whiptail is installed... --- automated install/basic-install.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index d8899ce3..61665fdb 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -1013,8 +1013,6 @@ update_dialogs() { } main() { - # Check if SELinux is Enforcing - checkSelinux # Check arguments for the undocumented flags for var in "$@"; do @@ -1051,6 +1049,9 @@ main() { # Install packages used by this installation script install_dependent_packages INSTALLER_DEPS[@] + # Check if SELinux is Enforcing + checkSelinux + if [[ "${reconfigure}" == true ]]; then echo "::: --reconfigure passed to install script. Not downloading/updating local repos" else From 662d4506514543a596810a77b3dcf25239f438d5 Mon Sep 17 00:00:00 2001 From: Promofaux Date: Fri, 23 Dec 2016 16:58:58 +0000 Subject: [PATCH 56/89] adjust size of whiptail dialog --- automated install/basic-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 61665fdb..1b1070e2 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -941,7 +941,7 @@ checkSelinux() { printf "\n::: SELinux Support Detected.." getenforce | grep 'Enforcing' if [ $? -eq 0 ]; then - if (whiptail --title "SELinux Detected" --yesno "SELinux is being Enforced on your system. \n Pi-hole does not support SELinux at this time...\nContinue with SELinux Enforcing?" 8 78); then + if (whiptail --title "SELinux Detected" --yesno "SELinux is being Enforced on your system. \nPi-hole does not support SELinux at this time...\nContinue with SELinux Enforcing?" ${r} ${c}); then printf "\n::: Continuing installation with SELinux Enforcing.." printf "\n::: Please refer to official SELinux documentation to create a custom policy." else From c45c3a72b517767cc7f11fc0804ce09812ae3a03 Mon Sep 17 00:00:00 2001 From: Promofaux Date: Fri, 23 Dec 2016 17:06:33 +0000 Subject: [PATCH 57/89] words words words --- automated install/basic-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 1b1070e2..98376e76 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -941,7 +941,7 @@ checkSelinux() { printf "\n::: SELinux Support Detected.." getenforce | grep 'Enforcing' if [ $? -eq 0 ]; then - if (whiptail --title "SELinux Detected" --yesno "SELinux is being Enforced on your system. \nPi-hole does not support SELinux at this time...\nContinue with SELinux Enforcing?" ${r} ${c}); then + if (whiptail --title "SELinux Detected" --yesno "SELinux is being Enforced on your system!\n\nPi-hole currently does not support SELinux, but you may still continue with the installation.\n\nNote: Admin UI Will not function fully without setting your policies correctly\n\nContinue installing Pi-hole?" ${r} ${c}); then printf "\n::: Continuing installation with SELinux Enforcing.." printf "\n::: Please refer to official SELinux documentation to create a custom policy." else From 00aff6a906dcef87382b50c0238626b20022faa6 Mon Sep 17 00:00:00 2001 From: Promofaux Date: Fri, 23 Dec 2016 17:16:03 +0000 Subject: [PATCH 58/89] maybe --- automated install/basic-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 98376e76..56960d24 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -939,8 +939,8 @@ updatePihole() { checkSelinux() { if [ -x "$(command -v getenforce)" ]; then printf "\n::: SELinux Support Detected.." - getenforce | grep 'Enforcing' - if [ $? -eq 0 ]; then + enforceMode=$(getenforce) + if [[ "${enforceMode}" == "Enforcing" ]]; then if (whiptail --title "SELinux Detected" --yesno "SELinux is being Enforced on your system!\n\nPi-hole currently does not support SELinux, but you may still continue with the installation.\n\nNote: Admin UI Will not function fully without setting your policies correctly\n\nContinue installing Pi-hole?" ${r} ${c}); then printf "\n::: Continuing installation with SELinux Enforcing.." printf "\n::: Please refer to official SELinux documentation to create a custom policy." From 15c674ba293b31bf45d1eb050c1ced9cb2cf430a Mon Sep 17 00:00:00 2001 From: Promofaux Date: Fri, 23 Dec 2016 17:20:33 +0000 Subject: [PATCH 59/89] verbosity --- automated install/basic-install.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 56960d24..82ea6cb1 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -938,13 +938,16 @@ updatePihole() { checkSelinux() { if [ -x "$(command -v getenforce)" ]; then - printf "\n::: SELinux Support Detected.." + echo -n "\n::: SELinux Support Detected... Mode: " enforceMode=$(getenforce) + echo "${enforceMode}" if [[ "${enforceMode}" == "Enforcing" ]]; then - if (whiptail --title "SELinux Detected" --yesno "SELinux is being Enforced on your system!\n\nPi-hole currently does not support SELinux, but you may still continue with the installation.\n\nNote: Admin UI Will not function fully without setting your policies correctly\n\nContinue installing Pi-hole?" ${r} ${c}); then + echo "Enforcing" + if (whiptail --title "SELinux Enforcing Detected" --yesno "SELinux is being Enforced on your system!\n\nPi-hole currently does not support SELinux, but you may still continue with the installation.\n\nNote: Admin UI Will not function fully without setting your policies correctly\n\nContinue installing Pi-hole?" ${r} ${c}); then printf "\n::: Continuing installation with SELinux Enforcing.." printf "\n::: Please refer to official SELinux documentation to create a custom policy." else + printf "\n::: Not continuing install after SELinux Enforcing detected" exit 1 fi fi From 4c131b8c282465a6a7b3470f7236ba7910870922 Mon Sep 17 00:00:00 2001 From: Promofaux Date: Fri, 23 Dec 2016 17:21:23 +0000 Subject: [PATCH 60/89] newlines --- automated install/basic-install.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 82ea6cb1..e5f37152 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -938,7 +938,8 @@ updatePihole() { checkSelinux() { if [ -x "$(command -v getenforce)" ]; then - echo -n "\n::: SELinux Support Detected... Mode: " + echo ":::" + echo -n "::: SELinux Support Detected... Mode: " enforceMode=$(getenforce) echo "${enforceMode}" if [[ "${enforceMode}" == "Enforcing" ]]; then From 754f3359ec3018d1757cc5c8684fb5bd54c3dc69 Mon Sep 17 00:00:00 2001 From: Promofaux Date: Fri, 23 Dec 2016 17:22:31 +0000 Subject: [PATCH 61/89] too many echos spoil the branch --- automated install/basic-install.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index e5f37152..7aaf470c 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -943,7 +943,6 @@ checkSelinux() { enforceMode=$(getenforce) echo "${enforceMode}" if [[ "${enforceMode}" == "Enforcing" ]]; then - echo "Enforcing" if (whiptail --title "SELinux Enforcing Detected" --yesno "SELinux is being Enforced on your system!\n\nPi-hole currently does not support SELinux, but you may still continue with the installation.\n\nNote: Admin UI Will not function fully without setting your policies correctly\n\nContinue installing Pi-hole?" ${r} ${c}); then printf "\n::: Continuing installation with SELinux Enforcing.." printf "\n::: Please refer to official SELinux documentation to create a custom policy." From 972598503769bab811811d8732f72659545cee6f Mon Sep 17 00:00:00 2001 From: Promofaux Date: Fri, 23 Dec 2016 17:24:33 +0000 Subject: [PATCH 62/89] printf to echo. idgaf --- automated install/basic-install.sh | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 7aaf470c..165ed5f3 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -944,10 +944,12 @@ checkSelinux() { echo "${enforceMode}" if [[ "${enforceMode}" == "Enforcing" ]]; then if (whiptail --title "SELinux Enforcing Detected" --yesno "SELinux is being Enforced on your system!\n\nPi-hole currently does not support SELinux, but you may still continue with the installation.\n\nNote: Admin UI Will not function fully without setting your policies correctly\n\nContinue installing Pi-hole?" ${r} ${c}); then - printf "\n::: Continuing installation with SELinux Enforcing.." - printf "\n::: Please refer to official SELinux documentation to create a custom policy." + echo ":::" + echo "::: Continuing installation with SELinux Enforcing.." + echo "::: Please refer to official SELinux documentation to create a custom policy." else - printf "\n::: Not continuing install after SELinux Enforcing detected" + echo ":::" + echo "::: Not continuing install after SELinux Enforcing detected" exit 1 fi fi From abd1fedc9d2e7adbc51fc3ff83a051e23dfead70 Mon Sep 17 00:00:00 2001 From: Promofaux Date: Fri, 23 Dec 2016 17:25:55 +0000 Subject: [PATCH 63/89] . --- automated install/basic-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 165ed5f3..058a57e9 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -945,11 +945,11 @@ checkSelinux() { if [[ "${enforceMode}" == "Enforcing" ]]; then if (whiptail --title "SELinux Enforcing Detected" --yesno "SELinux is being Enforced on your system!\n\nPi-hole currently does not support SELinux, but you may still continue with the installation.\n\nNote: Admin UI Will not function fully without setting your policies correctly\n\nContinue installing Pi-hole?" ${r} ${c}); then echo ":::" - echo "::: Continuing installation with SELinux Enforcing.." + echo "::: Continuing installation with SELinux Enforcing." echo "::: Please refer to official SELinux documentation to create a custom policy." else echo ":::" - echo "::: Not continuing install after SELinux Enforcing detected" + echo "::: Not continuing install after SELinux Enforcing detected." exit 1 fi fi From fc8fcdbece3de9920c085a2d064f55b137c9893d Mon Sep 17 00:00:00 2001 From: Mcat12 Date: Fri, 23 Dec 2016 12:28:18 -0500 Subject: [PATCH 64/89] Add newline --- automated install/basic-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 058a57e9..0c28f725 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -949,7 +949,7 @@ checkSelinux() { echo "::: Please refer to official SELinux documentation to create a custom policy." else echo ":::" - echo "::: Not continuing install after SELinux Enforcing detected." + echo "::: Not continuing install after SELinux Enforcing detected.\n" exit 1 fi fi From 54afffed19694fb24891be047c4b9d918f28a353 Mon Sep 17 00:00:00 2001 From: Mcat12 Date: Fri, 23 Dec 2016 12:30:33 -0500 Subject: [PATCH 65/89] Remove newline Issue was already fixed --- automated install/basic-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 0c28f725..058a57e9 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -949,7 +949,7 @@ checkSelinux() { echo "::: Please refer to official SELinux documentation to create a custom policy." else echo ":::" - echo "::: Not continuing install after SELinux Enforcing detected.\n" + echo "::: Not continuing install after SELinux Enforcing detected." exit 1 fi fi From 9a62026830ca29de780c11b85951ddb75159a72e Mon Sep 17 00:00:00 2001 From: Promofaux Date: Fri, 23 Dec 2016 19:27:02 +0000 Subject: [PATCH 66/89] remove `DNS_BOGUS_PRIV` from setupVars.conf --- advanced/Scripts/webpage.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/advanced/Scripts/webpage.sh b/advanced/Scripts/webpage.sh index f6f8606f..38e82c14 100755 --- a/advanced/Scripts/webpage.sh +++ b/advanced/Scripts/webpage.sh @@ -69,7 +69,7 @@ SetWebPassword(){ SetDNSServers(){ # Remove setting from file (create backup setupVars.conf.bak) - sed -i.bak '/PIHOLE_DNS_1/d;/PIHOLE_DNS_2/d;/DNS_FQDN_REQUIRED/d;' /etc/pihole/setupVars.conf + sed -i.bak '/PIHOLE_DNS_1/d;/PIHOLE_DNS_2/d;/DNS_FQDN_REQUIRED/d;/DNS_BOGUS_PRIV/d;' /etc/pihole/setupVars.conf # Save setting to file echo "PIHOLE_DNS_1=${args[2]}" >> /etc/pihole/setupVars.conf if [[ "${args[3]}" != "none" ]]; then From ce2e410468086284eae9208330f5ec9351dc5547 Mon Sep 17 00:00:00 2001 From: Promofaux Date: Fri, 23 Dec 2016 19:39:37 +0000 Subject: [PATCH 67/89] Accidentally a word. Updated link --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c982354c..311c8704 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ ## The multi-platform, network-wide ad blocker -Block ads for **all** your devices _without_ the need to install client-side software. The Pi-hole blocks ads the DNS-level, so all your devices are protected. +Block ads for **all** your devices _without_ the need to install client-side software. The Pi-hole blocks ads at the DNS-level, so all your devices are protected. - Web Browsers - Cell Phones @@ -53,7 +53,7 @@ wget -O basic-install.sh https://install.pi-hole.net bash basic-install.sh ``` -Once installed, [configure your router to have **DHCP clients use the Pi as their DNS server**](http://pi-hole.net/faq/can-i-set-the-pi-hole-to-be-the-dns-server-at-my-router-so-i-dont-have-to-change-settings-for-my-devices/) and then any device that connects to your network will have ads blocked without any further configuration. Alternatively, you can manually set each device to [use the Raspberry Pi as its DNS server](http://pi-hole.net/faq/how-do-i-use-the-pi-hole-as-my-dns-server/). +Once installed, [configure your router to have **DHCP clients use the Pi as their DNS server**](https://discourse.pi-hole.net/t/how-do-i-configure-my-devices-to-use-pi-hole-as-their-dns-server/245) and then any device that connects to your network will have ads blocked without any further configuration. Alternatively, you can manually set each device to [use the Raspberry Pi as its DNS server](http://pi-hole.net/faq/how-do-i-use-the-pi-hole-as-my-dns-server/). ## Installing the Pi-hole (Click to Watch!)

From d89254fedfee66067f2e09629ca4ca170a7b297f Mon Sep 17 00:00:00 2001 From: Promofaux Date: Fri, 23 Dec 2016 19:49:39 +0000 Subject: [PATCH 68/89] replace 1-10 checkboxes to tidy up appearance --- .github/ISSUE_TEMPLATE.md | 11 +---------- .github/PULL_REQUEST_TEMPLATE.md | 11 +---------- 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 07fc4352..c985b972 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -6,16 +6,7 @@ **How familiar are you with the codebase?:** -- [] 1 (very unfamiliar) -- [] 2 -- [] 3 -- [] 4 -- [] 5 -- [] 6 -- [] 7 -- [] 8 -- [] 9 -- [] 10 (very familiar) +_{replace this text with a number from 1 to 10, with 1 being not familiar, and 10 being very familiar}_ --- **[FEATURE REQUEST | QUESTION | OTHER]:** diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 9d6310d0..424bbc78 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -10,16 +10,7 @@ **How familiar are you with the codebase?:** -- [] 1 (very unfamiliar) -- [] 2 -- [] 3 -- [] 4 -- [] 5 -- [] 6 -- [] 7 -- [] 8 -- [] 9 -- [] 10 (very familiar) +_{replace this text with a number from 1 to 10, with 1 being not familiar, and 10 being very familiar}_ --- _{replace this line with your pull request content}_ From 04a9791be2e39e1923d05850276902fb5c6ce2c4 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Fri, 23 Dec 2016 23:41:40 +0100 Subject: [PATCH 69/89] Save empty password --- advanced/Scripts/webpage.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/advanced/Scripts/webpage.sh b/advanced/Scripts/webpage.sh index 38e82c14..b6d500cd 100755 --- a/advanced/Scripts/webpage.sh +++ b/advanced/Scripts/webpage.sh @@ -61,6 +61,7 @@ SetWebPassword(){ echo "WEBPASSWORD=${hash}" >> /etc/pihole/setupVars.conf echo "New password set" else + echo "WEBPASSWORD=" >> /etc/pihole/setupVars.conf echo "Password removed" fi From 0276c72fe2bf9ed80cfbe923437d3f282c310bb4 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sat, 24 Dec 2016 15:05:57 +0100 Subject: [PATCH 70/89] replace 'git -C' with long version (see #1009) --- advanced/Scripts/update.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) mode change 100644 => 100755 advanced/Scripts/update.sh diff --git a/advanced/Scripts/update.sh b/advanced/Scripts/update.sh old mode 100644 new mode 100755 index 10728cd8..929d8a25 --- a/advanced/Scripts/update.sh +++ b/advanced/Scripts/update.sh @@ -23,8 +23,8 @@ is_repo() { # Use git to check if directory is currently under VCS, return the value local directory="${1}" - git -C "${directory}" status --short &> /dev/null - return + curdir=$PWD; cd $directory; git status --short &> /dev/null; rc=$?; cd $curdir + return $rc } prep_repo() { From b020010f0daa191b419ae4765c03be851dd017f4 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sat, 24 Dec 2016 15:08:30 +0100 Subject: [PATCH 71/89] Removed some of the &> /dev/null --- advanced/Scripts/update.sh | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/advanced/Scripts/update.sh b/advanced/Scripts/update.sh index 929d8a25..e5fd1526 100755 --- a/advanced/Scripts/update.sh +++ b/advanced/Scripts/update.sh @@ -40,22 +40,19 @@ make_repo() { local remoteRepo="${2}" local directory="${1}" - (prep_repo "${directory}" && git clone -q --depth 1 "${remoteRepo}" "${directory}" > /dev/null) + (prep_repo "${directory}" && git clone -q --depth 1 "${remoteRepo}" "${directory}") return } update_repo() { local directory="${1}" - local retVal=0 # Pull the latest commits - # Stash all files not tracked for later retrieval - git -C "${directory}" stash --all --quiet &> /dev/null || ${retVal}=1 + git -C "${directory}" stash --all --quiet # Force a clean working directory for cloning - git -C "${directory}" clean --force -d &> /dev/null || ${retVal}=1 + git -C "${directory}" clean --force -d # Fetch latest changes and apply - git -C "${directory}" pull --quiet &> /dev/null || ${retVal}=1 - return ${retVal} + git -C "${directory}" pull --quiet } getGitFiles() { From c449a1c0e0db0e4d630285fc77e126815fded680 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sat, 24 Dec 2016 15:19:39 +0100 Subject: [PATCH 72/89] Added GitCheckUpdateAvail() --- advanced/Scripts/update.sh | 39 +++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/advanced/Scripts/update.sh b/advanced/Scripts/update.sh index e5fd1526..8b289227 100755 --- a/advanced/Scripts/update.sh +++ b/advanced/Scripts/update.sh @@ -23,7 +23,11 @@ is_repo() { # Use git to check if directory is currently under VCS, return the value local directory="${1}" - curdir=$PWD; cd $directory; git status --short &> /dev/null; rc=$?; cd $curdir + curdir=$PWD; + cd $directory; + git status --short &> /dev/null; + rc=$?; + cd $curdir return $rc } @@ -73,29 +77,42 @@ getGitFiles() { fi } +GitCheckUpdateAvail() { + local directory="${1}" + curdir=$PWD; + cd "${directory}" + + # Fetch latest changes in this repo + git fetch origin + status="$(git status -sb)" + + cd "${curdir}" + + if [[ $status == *"behind"* ]]; then + # Local branch is behind remote branch -> Update + return 1 + else + # Local branch is up-to-date + return 0 + fi +} + main() { local pihole_version_current local pihole_version_latest local web_version_current local web_version_latest - if ! is_repo "${PI_HOLE_FILES_DIR}" || ! is_repo "${ADMIN_INTERFACE_DIR}" ; then #This is unlikely + #This is unlikely + if ! is_repo "${PI_HOLE_FILES_DIR}" || ! is_repo "${ADMIN_INTERFACE_DIR}" ; then echo "::: Critical Error: One or more Pi-Hole repos are missing from system!" echo "::: Please re-run install script from https://github.com/pi-hole/pi-hole" exit 1; fi echo "::: Checking for updates..." - # Checks Pi-hole version string in format vX.X.X - pihole_version_current="$(/usr/local/bin/pihole version --pihole --current)" - pihole_version_latest="$(/usr/local/bin/pihole version --pihole --latest)" - web_version_current="$(/usr/local/bin/pihole version --admin --current)" - web_version_latest="$(/usr/local/bin/pihole version --admin --latest)" - if [[ "${pihole_version_latest}" == "-1" || "${web_version_latest}" == "-1" ]]; then - echo "*** Unable to contact GitHub for latest version. Please try again later, contact support if this continues." - exit 1 - fi + # Logic # If latest versions are blank - we've probably hit Github rate limit (stop running `pihole -up so often!): From 65c35a5530468c604ea9f212aa928fb96fb8e935 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sat, 24 Dec 2016 15:32:25 +0100 Subject: [PATCH 73/89] Use new subroutine to determine if updates are available --- advanced/Scripts/update.sh | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/advanced/Scripts/update.sh b/advanced/Scripts/update.sh index 8b289227..14ca74de 100755 --- a/advanced/Scripts/update.sh +++ b/advanced/Scripts/update.sh @@ -86,14 +86,17 @@ GitCheckUpdateAvail() { git fetch origin status="$(git status -sb)" + # Change back to original directory cd "${curdir}" if [[ $status == *"behind"* ]]; then # Local branch is behind remote branch -> Update - return 1 - else - # Local branch is up-to-date return 0 + else + # Local branch is up-to-date or in a situation + # where this updater cannot be used (like on a + # branch that exists only locally) + return 1 fi } @@ -112,7 +115,18 @@ main() { echo "::: Checking for updates..." + if GitCheckUpdateAvail "${PI_HOLE_FILES_DIR}" ; then + core_update=true + echo "Pi-hole Core update available" + fi + if GitCheckUpdateAvail "${ADMIN_INTERFACE_DIR}" ; then + web_update=true + echo "Web Interface update available" + fi + + + exit # Logic # If latest versions are blank - we've probably hit Github rate limit (stop running `pihole -up so often!): From 4632b0f797e3cab7d24ce62e07bdf202e3e25cca Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sat, 24 Dec 2016 15:40:31 +0100 Subject: [PATCH 74/89] Updated updater logic --- advanced/Scripts/update.sh | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/advanced/Scripts/update.sh b/advanced/Scripts/update.sh index 14ca74de..3cbc1d65 100755 --- a/advanced/Scripts/update.sh +++ b/advanced/Scripts/update.sh @@ -117,20 +117,21 @@ main() { if GitCheckUpdateAvail "${PI_HOLE_FILES_DIR}" ; then core_update=true - echo "Pi-hole Core update available" + echo "::: Pi-hole Core: update available" + else + core_update=false + echo "::: Pi-hole Core: up to date" fi if GitCheckUpdateAvail "${ADMIN_INTERFACE_DIR}" ; then web_update=true - echo "Web Interface update available" + echo "::: Web Interface: update available" + else + web_update=false + echo "::: Web Interface: up to date" fi - - exit - # Logic - # If latest versions are blank - we've probably hit Github rate limit (stop running `pihole -up so often!): - # Update anyway # If Core up to date AND web up to date: # Do nothing # If Core up to date AND web NOT up to date: @@ -140,46 +141,40 @@ main() { # if Core NOT up to date AND web NOT up to date: # pull pihole repo run install --unattended - if [[ "${pihole_version_current}" == "${pihole_version_latest}" ]] && [[ "${web_version_current}" == "${web_version_latest}" ]]; then - echo ":::" - echo "::: Pi-hole version is $pihole_version_current" - echo "::: Web Admin version is $web_version_current" + if ! ${core_update} && ! ${web_update} ; then echo ":::" echo "::: Everything is up to date!" exit 0 - elif [[ "${pihole_version_current}" == "${pihole_version_latest}" ]] && [[ "${web_version_current}" < "${web_version_latest}" ]]; then + elif ! ${core_update} && ${web_update} ; then echo ":::" echo "::: Pi-hole Web Admin files out of date" getGitFiles "${ADMIN_INTERFACE_DIR}" "${ADMIN_INTERFACE_GIT_URL}" - web_updated=true - - elif [[ "${pihole_version_current}" < "${pihole_version_latest}" ]] && [[ "${web_version_current}" == "${web_version_latest}" ]]; then + elif ${core_update} && ! ${web_update} ; then + echo ":::" echo "::: Pi-hole core files out of date" getGitFiles "${PI_HOLE_FILES_DIR}" "${PI_HOLE_GIT_URL}" /etc/.pihole/automated\ install/basic-install.sh --reconfigure --unattended || echo "Unable to complete update, contact Pi-hole" && exit 1 - core_updated=true - elif [[ "${pihole_version_current}" < "${pihole_version_latest}" ]] && [[ "${web_version_current}" < "${web_version_latest}" ]]; then + elif ${core_update} && ${web_update} ; then + echo ":::" echo "::: Updating Everything" getGitFiles "${PI_HOLE_FILES_DIR}" "${PI_HOLE_GIT_URL}" /etc/.pihole/automated\ install/basic-install.sh --unattended || echo "Unable to complete update, contact Pi-hole" && exit 1 - web_updated=true - core_updated=true else echo "*** Update script has malfunctioned, fallthrough reached. Please contact support" exit 1 fi - if [[ "${web_updated}" == true ]]; then + if [[ "${web_update}" == true ]]; then web_version_current="$(/usr/local/bin/pihole version --admin --current)" echo ":::" echo "::: Web Admin version is now at ${web_version_current}" echo "::: If you had made any changes in '/var/www/html/admin/', they have been stashed using 'git stash'" fi - if [[ "${core_updated}" == true ]]; then + if [[ "${core_update}" == true ]]; then pihole_version_current="$(/usr/local/bin/pihole version --pihole --current)" echo ":::" echo "::: Pi-hole version is now at ${pihole_version_current}" From 15db1ffdd51760ab44f5a52faefd301ef42fd53c Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sat, 24 Dec 2016 15:48:43 +0100 Subject: [PATCH 75/89] Remove variables not needed any more --- advanced/Scripts/update.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/advanced/Scripts/update.sh b/advanced/Scripts/update.sh index 3cbc1d65..bd8d6b26 100755 --- a/advanced/Scripts/update.sh +++ b/advanced/Scripts/update.sh @@ -102,9 +102,7 @@ GitCheckUpdateAvail() { main() { local pihole_version_current - local pihole_version_latest local web_version_current - local web_version_latest #This is unlikely if ! is_repo "${PI_HOLE_FILES_DIR}" || ! is_repo "${ADMIN_INTERFACE_DIR}" ; then From e647efd47174ee474224d400f7c27d842726b73c Mon Sep 17 00:00:00 2001 From: Dan Schaper Date: Sat, 24 Dec 2016 11:24:20 -0800 Subject: [PATCH 76/89] Shell script, not javascript Signed-off-by: Dan Schaper --- advanced/Scripts/update.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/advanced/Scripts/update.sh b/advanced/Scripts/update.sh index bd8d6b26..ae73caa4 100755 --- a/advanced/Scripts/update.sh +++ b/advanced/Scripts/update.sh @@ -23,10 +23,10 @@ is_repo() { # Use git to check if directory is currently under VCS, return the value local directory="${1}" - curdir=$PWD; - cd $directory; - git status --short &> /dev/null; - rc=$?; + curdir=$PWD + cd $directory + git status --short &> /dev/null + rc=$? cd $curdir return $rc } From 68c17b26dca9bc3afc3255652bc050ce82146838 Mon Sep 17 00:00:00 2001 From: Dan Schaper Date: Sat, 24 Dec 2016 11:41:42 -0800 Subject: [PATCH 77/89] Bashisms Signed-off-by: Dan Schaper --- advanced/Scripts/update.sh | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/advanced/Scripts/update.sh b/advanced/Scripts/update.sh index ae73caa4..43210a28 100755 --- a/advanced/Scripts/update.sh +++ b/advanced/Scripts/update.sh @@ -22,13 +22,15 @@ readonly PI_HOLE_FILES_DIR="/etc/.pihole" is_repo() { # Use git to check if directory is currently under VCS, return the value local directory="${1}" + local curdir + local rc - curdir=$PWD - cd $directory + curdir="${PWD}" + cd "${directory}" || { echo "Unable to change to ${directory}, exiting."; exit 1; } git status --short &> /dev/null rc=$? - cd $curdir - return $rc + cd "${curdir}" || { echo "Unable to change to ${curdir}, exiting."; exit 1; } + return "${rc}" } prep_repo() { From 3365ef7aaa60595039d85db2ee40e3e9e4b9c535 Mon Sep 17 00:00:00 2001 From: Dan Schaper Date: Sat, 24 Dec 2016 11:54:04 -0800 Subject: [PATCH 78/89] `is_repo()` returns values to caller, silence function. Signed-off-by: Dan Schaper --- advanced/Scripts/update.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/advanced/Scripts/update.sh b/advanced/Scripts/update.sh index 43210a28..9f9ea320 100755 --- a/advanced/Scripts/update.sh +++ b/advanced/Scripts/update.sh @@ -26,10 +26,10 @@ is_repo() { local rc curdir="${PWD}" - cd "${directory}" || { echo "Unable to change to ${directory}, exiting."; exit 1; } + cd "${directory}" &> /dev/null || return 1 git status --short &> /dev/null rc=$? - cd "${curdir}" || { echo "Unable to change to ${curdir}, exiting."; exit 1; } + cd "${curdir}" &> /dev/null || return 1 return "${rc}" } From c2b8bed3a889f81d2b40e07f189652eaf7ad4f47 Mon Sep 17 00:00:00 2001 From: Dan Schaper Date: Sat, 24 Dec 2016 14:43:57 -0800 Subject: [PATCH 79/89] Get the IP address that is the source for the default route out. Fixes interfaces with multiple IP's assigned getting the wrong IPv4 chosen. Signed-off-by: Dan Schaper --- automated install/basic-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 0a56b06e..bbebe2f0 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -182,7 +182,7 @@ getGitFiles() { find_IPv4_information() { # Find IP used to route to outside world IPv4dev=$(ip route get 8.8.8.8 | awk '{for(i=1;i<=NF;i++)if($i~/dev/)print $(i+1)}') - IPV4_ADDRESS=$(ip -o -f inet addr show dev "$IPv4dev" | awk '{print $4}' | awk 'END {print}') + IPV4_ADDRESS=$(ip route get 8.8.8.8| awk '{print $7}') IPv4gw=$(ip route get 8.8.8.8 | awk '{print $3}') } From 1d608b204a75dd9b59a9fac6789b7b96ef62c27e Mon Sep 17 00:00:00 2001 From: Dan Schaper Date: Sat, 24 Dec 2016 14:50:50 -0800 Subject: [PATCH 80/89] Only allow UP interfaces to be selected. (DOWN interfaces would have not given IP addresses anyways.) Signed-off-by: Dan Schaper --- automated install/basic-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index bbebe2f0..ab14f519 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -187,8 +187,8 @@ find_IPv4_information() { } get_available_interfaces() { - # Get available interfaces. Consider only getting UP interfaces in the future, and leaving DOWN interfaces out of list. - availableInterfaces=$(ip -o link | awk '{print $2}' | grep -v "lo" | cut -d':' -f1 | cut -d'@' -f1) + # Get available UP interfaces. + availableInterfaces=$(ip -o link | grep "state UP" | awk '{print $2}' | cut -d':' -f1 | cut -d'@' -f1) } welcomeDialogs() { From 6ff79835da7ef3c565f159e877b83ca37648c476 Mon Sep 17 00:00:00 2001 From: Dan Schaper Date: Sat, 24 Dec 2016 15:08:39 -0800 Subject: [PATCH 81/89] If only one interface available, don't show whiptail, just use that interface. Signed-off-by: Dan Schaper --- automated install/basic-install.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index ab14f519..47c4925c 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -247,6 +247,11 @@ chooseInterface() { # Loop sentinel variable local firstLoop=1 + if [[ $(echo ${availableInterfaces} | wc -l) -eq 1 ]]; then + PIHOLE_INTERFACE=${availableInterfaces} + return + fi + while read -r line; do mode="OFF" if [[ ${firstLoop} -eq 1 ]]; then From da413834762775e48e20d0f7574a6976a878adc4 Mon Sep 17 00:00:00 2001 From: Dan Schaper Date: Sat, 24 Dec 2016 15:26:05 -0800 Subject: [PATCH 82/89] Check IPv6 to see if the route is reachable, if not, don't use. Signed-off-by: Dan Schaper --- automated install/basic-install.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 0a56b06e..2be4198c 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -273,8 +273,11 @@ chooseInterface() { useIPv6dialog() { # Show the IPv6 address used for blocking - IPV6_ADDRESS=$(ip -6 route get 2001:4860:4860::8888 | awk -F " " '{ for(i=1;i<=NF;i++) if ($i == "src") print $(i+1) }') - whiptail --msgbox --backtitle "IPv6..." --title "IPv6 Supported" "$IPV6_ADDRESS will be used to block ads." ${r} ${c} + IPV6_ADDRESS=$(ip -6 route get 2001:4860:4860::8888 | grep -v "unreachable" | awk -F " " '{ for(i=1;i<=NF;i++) if ($i == "src") print $(i+1) }') + + if [[ ! -z "${IPV6_ADDRESS}" ]]; then + whiptail --msgbox --backtitle "IPv6..." --title "IPv6 Supported" "$IPV6_ADDRESS will be used to block ads." ${r} ${c} + fi } From 5e28e6b9ac66f85f1310b582755eb3ba51973a92 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 25 Dec 2016 01:17:46 +0100 Subject: [PATCH 83/89] Removed remaining git -C --- advanced/Scripts/update.sh | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/advanced/Scripts/update.sh b/advanced/Scripts/update.sh index 9f9ea320..2abbca22 100755 --- a/advanced/Scripts/update.sh +++ b/advanced/Scripts/update.sh @@ -52,13 +52,18 @@ make_repo() { update_repo() { local directory="${1}" + local curdir + + curdir="${PWD}" + cd "${directory}" &> /dev/null || return 1 # Pull the latest commits # Stash all files not tracked for later retrieval - git -C "${directory}" stash --all --quiet + git stash --all --quiet # Force a clean working directory for cloning - git -C "${directory}" clean --force -d + git clean --force -d # Fetch latest changes and apply - git -C "${directory}" pull --quiet + git pull --quiet + cd "${curdir}" &> /dev/null || return 1 } getGitFiles() { From e15548cbf5b792ea13cfb7db3aaf62fb28b62fa4 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 25 Dec 2016 01:18:58 +0100 Subject: [PATCH 84/89] Add --quiet to git fetch in GitCheckUpdateAvail() --- advanced/Scripts/update.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/advanced/Scripts/update.sh b/advanced/Scripts/update.sh index 2abbca22..a2a5e8dc 100755 --- a/advanced/Scripts/update.sh +++ b/advanced/Scripts/update.sh @@ -90,7 +90,7 @@ GitCheckUpdateAvail() { cd "${directory}" # Fetch latest changes in this repo - git fetch origin + git fetch --quiet origin status="$(git status -sb)" # Change back to original directory From a0e0465036eb4fab12851165d97ce709c0e73880 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 25 Dec 2016 17:32:59 +0100 Subject: [PATCH 85/89] Revert "Merge pull request #1018 from pi-hole/diginc-patch-1" This reverts commit ff3c36a7a246d46e3cff5b1b3a89f0288352ce78, reversing changes made to 9355a8ad0e335efd7667d202e07b4d31781c388e. --- advanced/pihole.cron | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/advanced/pihole.cron b/advanced/pihole.cron index cb9965f0..8311acfb 100644 --- a/advanced/pihole.cron +++ b/advanced/pihole.cron @@ -13,15 +13,14 @@ # scripts, any changes made to this file will be overwritten when the softare # is updated or re-installed. Please make any changes to the appropriate crontab # or other cron file snippets. -PATH="$PATH:/usr/local/bin/" # Pi-hole: Update the ad sources once a week on Sunday at 01:59 # Download any updates from the adlists -59 1 * * 7 root pihole updateGravity +59 1 * * 7 root PATH="$PATH:/usr/local/bin/" pihole updateGravity # Pi-hole: Update Pi-hole! Uncomment to enable auto update -#30 2 * * 7 root pihole updatePihole +#30 2 * * 7 root PATH="$PATH:/usr/local/bin/" pihole updatePihole # Pi-hole: Flush the log daily at 00:00 so it doesn't get out of control # Stats will be viewable in the Web interface thanks to the cron job above -00 00 * * * root pihole flush +00 00 * * * root PATH="$PATH:/usr/local/bin/" pihole flush From ca056d32d2c29f90252635b74ece15f14b098bcf Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 25 Dec 2016 21:22:59 +0100 Subject: [PATCH 86/89] Remove git -C --- advanced/Scripts/update.sh | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) mode change 100644 => 100755 advanced/Scripts/update.sh diff --git a/advanced/Scripts/update.sh b/advanced/Scripts/update.sh old mode 100644 new mode 100755 index 10728cd8..5459ec04 --- a/advanced/Scripts/update.sh +++ b/advanced/Scripts/update.sh @@ -22,9 +22,15 @@ readonly PI_HOLE_FILES_DIR="/etc/.pihole" is_repo() { # Use git to check if directory is currently under VCS, return the value local directory="${1}" + local curdir + local rc - git -C "${directory}" status --short &> /dev/null - return + curdir="${PWD}" + cd "${directory}" &> /dev/null || return 1 + git status --short &> /dev/null + rc=$? + cd "${curdir}" &> /dev/null || return 1 + return $rc } prep_repo() { @@ -46,16 +52,20 @@ make_repo() { update_repo() { local directory="${1}" - local retVal=0 + local curdir # Pull the latest commits + curdir="${PWD}" + cd "${directory}" &> /dev/null || return 1 # Stash all files not tracked for later retrieval - git -C "${directory}" stash --all --quiet &> /dev/null || ${retVal}=1 + git stash --all --quiet &> /dev/null # Force a clean working directory for cloning - git -C "${directory}" clean --force -d &> /dev/null || ${retVal}=1 + git clean --force -d &> /dev/null # Fetch latest changes and apply - git -C "${directory}" pull --quiet &> /dev/null || ${retVal}=1 - return ${retVal} + git pull --quiet &> /dev/null + cd "{$curdir}" &> /dev/null || return 1 + + return } getGitFiles() { From e50947eb58be3e41079085bbe7caff99c00d9347 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 25 Dec 2016 21:26:06 +0100 Subject: [PATCH 87/89] mend --- advanced/Scripts/update.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/advanced/Scripts/update.sh b/advanced/Scripts/update.sh index 5459ec04..eb6ccd29 100755 --- a/advanced/Scripts/update.sh +++ b/advanced/Scripts/update.sh @@ -63,7 +63,7 @@ update_repo() { git clean --force -d &> /dev/null # Fetch latest changes and apply git pull --quiet &> /dev/null - cd "{$curdir}" &> /dev/null || return 1 + cd "${curdir}" &> /dev/null || return 1 return } From 4eedf8a746c86432bc8bf8be4af42e851843cc71 Mon Sep 17 00:00:00 2001 From: Dan Schaper Date: Tue, 27 Dec 2016 10:59:24 -0800 Subject: [PATCH 88/89] Always update package cache where we can. Signed-off-by: Dan Schaper --- automated install/basic-install.sh | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 3bda5b58..ea0d59bf 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -79,7 +79,6 @@ if [[ $(command -v apt-get) ]]; then #Debian Family ############################################# PKG_MANAGER="apt-get" - PKG_CACHE="/var/lib/apt/lists/" UPDATE_PKG_CACHE="${PKG_MANAGER} update" PKG_INSTALL="${PKG_MANAGER} --yes --no-install-recommends install" # grep -c will return 1 retVal on 0 matches, block this throwing the set -e with an OR TRUE @@ -108,8 +107,8 @@ elif [ $(command -v rpm) ]; then else PKG_MANAGER="yum" fi - PKG_CACHE="/var/cache/${PKG_MANAGER}" - UPDATE_PKG_CACHE="${PKG_MANAGER} check-update" + # Fedora and family update cache on every PKG_INSTALL call, no need for a separate update. + UPDATE_PKG_CACHE=":" PKG_INSTALL="${PKG_MANAGER} install -y" PKG_COUNT="${PKG_MANAGER} check-update | egrep '(.i686|.x86|.noarch|.arm|.src)' | wc -l" INSTALLER_DEPS=(git iproute net-tools newt procps-ng) @@ -718,19 +717,13 @@ update_pacakge_cache() { #Running apt-get update/upgrade with minimal output can cause some issues with #requiring user input (e.g password for phpmyadmin see #218) - #Check to see if apt-get update has already been run today - #it needs to have been run at least once on new installs! - timestamp=$(stat -c %Y ${PKG_CACHE}) - timestampAsDate=$(date -d @"${timestamp}" "+%b %e") - today=$(date "+%b %e") + #Update package cache on apt based OSes. Do this every time since + #it's quick and packages can be updated at any time. - if [ ! "${today}" == "${timestampAsDate}" ]; then - #update package lists echo ":::" - echo -n "::: ${PKG_MANAGER} update has not been run today. Running now..." + echo -n "::: Updating local cache of available packages..." ${UPDATE_PKG_CACHE} &> /dev/null echo " done!" - fi } notify_package_updates_available() { From a328326e3920e387d1609306dbffc3004a514e44 Mon Sep 17 00:00:00 2001 From: Dan Schaper Date: Tue, 27 Dec 2016 11:41:07 -0800 Subject: [PATCH 89/89] Formatting Signed-off-by: Dan Schaper --- automated install/basic-install.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index ea0d59bf..1638829d 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -720,10 +720,10 @@ update_pacakge_cache() { #Update package cache on apt based OSes. Do this every time since #it's quick and packages can be updated at any time. - echo ":::" - echo -n "::: Updating local cache of available packages..." - ${UPDATE_PKG_CACHE} &> /dev/null - echo " done!" + echo ":::" + echo -n "::: Updating local cache of available packages..." + ${UPDATE_PKG_CACHE} &> /dev/null + echo " done!" } notify_package_updates_available() {