Merge branch 'pi-hole:master' into patch-1

This commit is contained in:
Jim Grisham 2021-11-28 14:08:01 -08:00 committed by GitHub
commit c93cf2781d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
58 changed files with 1464 additions and 1309 deletions

10
.github/dependabot.yml vendored Normal file
View file

@ -0,0 +1,10 @@
version: 2
updates:
- package-ecosystem: github-actions
directory: "/"
schedule:
interval: weekly
day: saturday
time: "10:00"
open-pull-requests-limit: 10
target-branch: developement

40
.github/workflows/codeql-analysis.yml vendored Normal file
View file

@ -0,0 +1,40 @@
name: "CodeQL"
on:
push:
branches:
- master
- development
pull_request:
branches:
- master
- development
schedule:
- cron: '32 11 * * 6'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
steps:
-
name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
-
name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: 'python'
-
name: Autobuild
uses: github/codeql-action/autobuild@v1
-
name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

View file

@ -5,21 +5,44 @@ on:
types: [opened, synchronize, reopened, ready_for_review] types: [opened, synchronize, reopened, ready_for_review]
jobs: jobs:
smoke-test:
if: github.event.pull_request.draft == false
runs-on: ubuntu-latest
steps:
-
name: Checkout repository
uses: actions/checkout@v2
-
name: Run Smoke Tests
run: |
# Ensure scripts in repository are executable
IFS=$'\n';
for f in $(find . -name '*.sh'); do if [[ ! -x $f ]]; then echo "$f is not executable" && FAIL=1; fi ;done
unset IFS;
# If FAIL is 1 then we fail.
[[ $FAIL == 1 ]] && exit 1 || echo "Smoke Tests Passed"
distro-test: distro-test:
if: github.event.pull_request.draft == false if: github.event.pull_request.draft == false
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: smoke-test
strategy: strategy:
matrix: matrix:
distro: [debian_9, debian_10, ubuntu_16, ubuntu_18, ubuntu_20, centos_7, centos_8, fedora_31, fedora_32] distro: [debian_9, debian_10, debian_11, ubuntu_16, ubuntu_18, ubuntu_20, ubuntu_21, centos_7, centos_8, fedora_33, fedora_34]
env: env:
DISTRO: ${{matrix.distro}} DISTRO: ${{matrix.distro}}
steps: steps:
- uses: actions/checkout@v1 -
- name: Set up Python 3.7 name: Checkout repository
uses: actions/checkout@v2
-
name: Set up Python 3.7
uses: actions/setup-python@v2 uses: actions/setup-python@v2
with: with:
python-version: 3.7 python-version: 3.7
- name: Install dependencies -
name: Install dependencies
run: pip install -r test/requirements.txt run: pip install -r test/requirements.txt
- name: Test with tox -
name: Test with tox
run: tox -c test/tox.${DISTRO}.ini run: tox -c test/tox.${DISTRO}.ini

67
.gitignore vendored
View file

@ -7,70 +7,5 @@ __pycache__
.tox .tox
.eggs .eggs
*.egg-info *.egg-info
.idea/
# Created by https://www.gitignore.io/api/jetbrains+iml
### JetBrains+iml ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# All idea files, with exceptions
.idea
!.idea/codeStyles/*
!.idea/codeStyleSettings.xml
# Sensitive or high-churn files:
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.xml
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
# Gradle:
.idea/**/gradle.xml
.idea/**/libraries
# CMake
cmake-build-debug/
# Mongo Explorer plugin:
.idea/**/mongoSettings.xml
## File-based project format:
*.iws
## Plugin-specific files:
# IntelliJ
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Ruby plugin and RubyMine
/.rakeTasks
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
### JetBrains+iml Patch ###
# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
*.iml *.iml
.idea/misc.xml
*.ipr
# End of https://www.gitignore.io/api/jetbrains+iml

View file

@ -1,25 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectCodeStyleSettingsManager">
<option name="PER_PROJECT_SETTINGS">
<value>
<option name="OTHER_INDENT_OPTIONS">
<value>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="8" />
<option name="TAB_SIZE" value="2" />
<option name="USE_TAB_CHARACTER" value="false" />
<option name="SMART_TABS" value="false" />
<option name="LABEL_INDENT_SIZE" value="0" />
<option name="LABEL_INDENT_ABSOLUTE" value="false" />
<option name="USE_RELATIVE_INDENTS" value="false" />
</value>
</option>
<MarkdownNavigatorCodeStyleSettings>
<option name="RIGHT_MARGIN" value="72" />
</MarkdownNavigatorCodeStyleSettings>
</value>
</option>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</component>
</project>

View file

@ -1,7 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<MarkdownNavigatorCodeStyleSettings>
<option name="RIGHT_MARGIN" value="72" />
</MarkdownNavigatorCodeStyleSettings>
</code_scheme>
</component>

View file

@ -1,5 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

View file

@ -1,5 +1,7 @@
<!-- markdownlint-configure-file { "MD004": { "style": "consistent" } } --> <!-- markdownlint-configure-file { "MD004": { "style": "consistent" } } -->
<!-- markdownlint-disable MD033 --> <!-- markdownlint-disable MD033 -->
#
<p align="center"> <p align="center">
<a href="https://pi-hole.net/"> <a href="https://pi-hole.net/">
<img src="https://pi-hole.github.io/graphics/Vortex/Vortex_with_Wordmark.svg" width="150" height="260" alt="Pi-hole"> <img src="https://pi-hole.github.io/graphics/Vortex/Vortex_with_Wordmark.svg" width="150" height="260" alt="Pi-hole">
@ -9,11 +11,9 @@
</p> </p>
<!-- markdownlint-enable MD033 --> <!-- markdownlint-enable MD033 -->
# The Pi-hole® is a [DNS sinkhole](https://en.wikipedia.org/wiki/DNS_Sinkhole) that protects your devices from unwanted content without installing any client-side software.
The Pi-hole® is a [DNS sinkhole](https://en.wikipedia.org/wiki/DNS_Sinkhole) that protects your devices from unwanted content, without installing any client-side software. - **Easy-to-install**: our versatile installer walks you through the process and takes less than ten minutes
- **Easy-to-install**: our versatile installer walks you through the process, and takes less than ten minutes
- **Resolute**: content is blocked in _non-browser locations_, such as ad-laden mobile apps and smart TVs - **Resolute**: content is blocked in _non-browser locations_, such as ad-laden mobile apps and smart TVs
- **Responsive**: seamlessly speeds up the feel of everyday browsing by caching DNS queries - **Responsive**: seamlessly speeds up the feel of everyday browsing by caching DNS queries
- **Lightweight**: runs smoothly with [minimal hardware and software requirements](https://docs.pi-hole.net/main/prerequisites/) - **Lightweight**: runs smoothly with [minimal hardware and software requirements](https://docs.pi-hole.net/main/prerequisites/)
@ -22,12 +22,10 @@ The Pi-hole® is a [DNS sinkhole](https://en.wikipedia.org/wiki/DNS_Sinkhole) th
- **Versatile**: can optionally function as a [DHCP server](https://discourse.pi-hole.net/t/how-do-i-use-pi-holes-built-in-dhcp-server-and-why-would-i-want-to/3026), ensuring *all* your devices are protected automatically - **Versatile**: can optionally function as a [DHCP server](https://discourse.pi-hole.net/t/how-do-i-use-pi-holes-built-in-dhcp-server-and-why-would-i-want-to/3026), ensuring *all* your devices are protected automatically
- **Scalable**: [capable of handling hundreds of millions of queries](https://pi-hole.net/2017/05/24/how-much-traffic-can-pi-hole-handle/) when installed on server-grade hardware - **Scalable**: [capable of handling hundreds of millions of queries](https://pi-hole.net/2017/05/24/how-much-traffic-can-pi-hole-handle/) when installed on server-grade hardware
- **Modern**: blocks ads over both IPv4 and IPv6 - **Modern**: blocks ads over both IPv4 and IPv6
- **Free**: open source software which helps ensure _you_ are the sole person in control of your privacy - **Free**: open source software that helps ensure _you_ are the sole person in control of your privacy
----- -----
Master [![Build Status](https://travis-ci.com/pi-hole/pi-hole.svg?branch=master)](https://travis-ci.com/pi-hole/pi-hole) Development [![Build Status](https://travis-ci.com/pi-hole/pi-hole.svg?branch=development)](https://travis-ci.com/pi-hole/pi-hole)
## One-Step Automated Install ## One-Step Automated Install
Those who want to get started quickly and conveniently may install Pi-hole using the following command: Those who want to get started quickly and conveniently may install Pi-hole using the following command:
@ -52,42 +50,46 @@ sudo bash basic-install.sh
wget -O basic-install.sh https://install.pi-hole.net wget -O basic-install.sh https://install.pi-hole.net
sudo bash basic-install.sh sudo bash basic-install.sh
``` ```
### Method 3: Using Docker to deploy Pi-hole
Please refer to the [Pi-hole docker repo](https://github.com/pi-hole/docker-pi-hole) to use the Official Docker Images.
## [Post-install: Make your network take advantage of Pi-hole](https://docs.pi-hole.net/main/post-install/) ## [Post-install: Make your network take advantage of Pi-hole](https://docs.pi-hole.net/main/post-install/)
Once the installer has been run, you will need to [configure your router to have **DHCP clients use Pi-hole 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) which ensures that all devices connecting to your network will have content blocked without any further intervention. Once the installer has been run, you will need to [configure your router to have **DHCP clients use Pi-hole 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) which ensures that all devices connecting to your network will have content blocked without any further intervention.
If your router does not support setting the DNS server, you can [use Pi-hole's built-in DHCP server](https://discourse.pi-hole.net/t/how-do-i-use-pi-holes-built-in-dhcp-server-and-why-would-i-want-to/3026); just be sure to disable DHCP on your router first (if it has that feature available). If your router does not support setting the DNS server, you can [use Pi-hole's built-in DHCP server](https://discourse.pi-hole.net/t/how-do-i-use-pi-holes-built-in-dhcp-server-and-why-would-i-want-to/3026); be sure to disable DHCP on your router first (if it has that feature available).
As a last resort, you can always manually set each device to use Pi-hole as their DNS server. As a last resort, you can manually set each device to use Pi-hole as their DNS server.
----- -----
## Pi-hole is free, but powered by your support ## Pi-hole is free but powered by your support
There are many reoccurring costs involved with maintaining free, open source, and privacy-respecting software; expenses which [our volunteer developers](https://github.com/orgs/pi-hole/people) pitch in to cover out-of-pocket. This is just one example of how strongly we feel about our software, as well as the importance of keeping it maintained. There are many reoccurring costs involved with maintaining free, open source, and privacy-respecting software; expenses which [our volunteer developers](https://github.com/orgs/pi-hole/people) pitch in to cover out-of-pocket. This is just one example of how strongly we feel about our software and the importance of keeping it maintained.
Make no mistake: **your support is absolutely vital to help keep us innovating!** Make no mistake: **your support is absolutely vital to help keep us innovating!**
### [Donations](https://pi-hole.net/donate) ### [Donations](https://pi-hole.net/donate)
Sending a donation using our Sponsor Button is **extremely helpful** in offsetting a portion of our monthly expenses: Donating using our Sponsor Button is **extremely helpful** in offsetting a portion of our monthly expenses:
### Alternative support ### Alternative support
If you'd rather not donate (_which is okay!_), there are other ways you can help support us: If you'd rather not donate (_which is okay!_), there are other ways you can help support us:
- [Patreon](https://patreon.com/pihole) _Become a patron for rewards_ - [GitHub Sponsors](https://github.com/sponsors/pi-hole/)
- [Patreon](https://patreon.com/pihole)
- [Hetzner Cloud](https://hetzner.cloud/?ref=7aceisRX3AzA) _affiliate link_
- [Digital Ocean](https://www.digitalocean.com/?refcode=344d234950e1) _affiliate link_ - [Digital Ocean](https://www.digitalocean.com/?refcode=344d234950e1) _affiliate link_
- [Stickermule](https://www.stickermule.com/unlock?ref_id=9127301701&utm_medium=link&utm_source=invite) _earn a $10 credit after your first purchase_ - [Stickermule](https://www.stickermule.com/unlock?ref_id=9127301701&utm_medium=link&utm_source=invite) _earn a $10 credit after your first purchase_
- [Amazon](http://www.amazon.com/exec/obidos/redirect-home/pihole09-20) _affiliate link_ - [Amazon US](http://www.amazon.com/exec/obidos/redirect-home/pihole09-20) _affiliate link_
- Spreading the word about our software, and how you have benefited from it - Spreading the word about our software and how you have benefited from it
### Contributing via GitHub ### Contributing via GitHub
We welcome _everyone_ to contribute to issue reports, suggest new features, and create pull requests. We welcome _everyone_ to contribute to issue reports, suggest new features, and create pull requests.
If you have something to add - anything from a typo through to a whole new feature, we're happy to check it out! Just make sure to fill out our template when submitting your request; the questions that it asks will help the volunteers quickly understand what you're aiming to achieve. If you have something to add - anything from a typo through to a whole new feature, we're happy to check it out! Just make sure to fill out our template when submitting your request; the questions it asks will help the volunteers quickly understand what you're aiming to achieve.
You'll find that the [install script](https://github.com/pi-hole/pi-hole/blob/master/automated%20install/basic-install.sh) and the [debug script](https://github.com/pi-hole/pi-hole/blob/master/advanced/Scripts/piholeDebug.sh) have an abundance of comments, which will help you better understand how Pi-hole works. They're also a valuable resource to those who want to learn how to write scripts or code a program! We encourage anyone who likes to tinker to read through it and submit a pull request for us to review. You'll find that the [install script](https://github.com/pi-hole/pi-hole/blob/master/automated%20install/basic-install.sh) and the [debug script](https://github.com/pi-hole/pi-hole/blob/master/advanced/Scripts/piholeDebug.sh) have an abundance of comments, which will help you better understand how Pi-hole works. They're also a valuable resource to those who want to learn how to write scripts or code a program! We encourage anyone who likes to tinker to read through it and submit a pull request for us to review.
@ -95,7 +97,9 @@ You'll find that the [install script](https://github.com/pi-hole/pi-hole/blob/ma
## Getting in touch with us ## Getting in touch with us
While we are primarily reachable on our [Discourse User Forum](https://discourse.pi-hole.net/), we can also be found on a variety of social media outlets. **Please be sure to check the FAQ's** before starting a new discussion, as we do not have the spare time to reply to every request for assistance. While we are primarily reachable on our [Discourse User Forum](https://discourse.pi-hole.net/), we can also be found on various social media outlets.
**Please be sure to check the FAQs** before starting a new discussion, as we do not have the spare time to reply to every request for assistance.
- [Frequently Asked Questions](https://discourse.pi-hole.net/c/faqs) - [Frequently Asked Questions](https://discourse.pi-hole.net/c/faqs)
- [Feature Requests](https://discourse.pi-hole.net/c/feature-requests?order=votes) - [Feature Requests](https://discourse.pi-hole.net/c/feature-requests?order=votes)
@ -106,15 +110,30 @@ While we are primarily reachable on our [Discourse User Forum](https://discourse
## Breakdown of Features ## Breakdown of Features
### [Faster-than-light Engine](https://github.com/pi-hole/ftl)
[FTLDNS](https://github.com/pi-hole/ftl) is a lightweight, purpose-built daemon used to provide statistics needed for the Web Interface, and its API can be easily integrated into your own projects. As the name implies, FTLDNS does this all *very quickly*!
Some of the statistics you can integrate include:
- Total number of domains being blocked
- Total number of DNS queries today
- Total number of ads blocked today
- Percentage of ads blocked
- Unique domains
- Queries forwarded (to your chosen upstream DNS server)
- Queries cached
- Unique clients
Access the API via [`telnet`](https://github.com/pi-hole/FTL), the Web (`admin/api.php`) and Command Line (`pihole -c -j`). You can find out [more details over here](https://discourse.pi-hole.net/t/pi-hole-api/1863).
### The Command Line Interface ### The Command Line Interface
The [pihole](https://docs.pi-hole.net/core/pihole-command/) command has all the functionality necessary to be able to fully administer the Pi-hole, without the need of the Web Interface. It's fast, user-friendly, and auditable by anyone with an understanding of `bash`. The [pihole](https://docs.pi-hole.net/core/pihole-command/) command has all the functionality necessary to fully administer the Pi-hole, without the need of the Web Interface. It's fast, user-friendly, and auditable by anyone with an understanding of `bash`.
![Pi-hole Blacklist Demo](https://pi-hole.github.io/graphics/Screenshots/blacklist-cli.gif)
Some notable features include: Some notable features include:
- [Whitelisting, Blacklisting and Regex](https://docs.pi-hole.net/core/pihole-command/#whitelisting-blacklisting-and-regex) - [Whitelisting, Blacklisting, and Regex](https://docs.pi-hole.net/core/pihole-command/#whitelisting-blacklisting-and-regex)
- [Debugging utility](https://docs.pi-hole.net/core/pihole-command/#debugger) - [Debugging utility](https://docs.pi-hole.net/core/pihole-command/#debugger)
- [Viewing the live log file](https://docs.pi-hole.net/core/pihole-command/#tail) - [Viewing the live log file](https://docs.pi-hole.net/core/pihole-command/#tail)
- [Updating Ad Lists](https://docs.pi-hole.net/core/pihole-command/#gravity) - [Updating Ad Lists](https://docs.pi-hole.net/core/pihole-command/#gravity)
@ -128,11 +147,9 @@ You can read our [Core Feature Breakdown](https://docs.pi-hole.net/core/pihole-c
This [optional dashboard](https://github.com/pi-hole/AdminLTE) allows you to view stats, change settings, and configure your Pi-hole. It's the power of the Command Line Interface, with none of the learning curve! This [optional dashboard](https://github.com/pi-hole/AdminLTE) allows you to view stats, change settings, and configure your Pi-hole. It's the power of the Command Line Interface, with none of the learning curve!
![Pi-hole Dashboard](https://pi-hole.github.io/graphics/Screenshots/pihole-dashboard.png)
Some notable features include: Some notable features include:
- Mobile friendly interface - Mobile-friendly interface
- Password protection - Password protection
- Detailed graphs and doughnut charts - Detailed graphs and doughnut charts
- Top lists of domains and clients - Top lists of domains and clients
@ -145,21 +162,3 @@ There are several ways to [access the dashboard](https://discourse.pi-hole.net/t
1. `http://pi.hole/admin/` (when using Pi-hole as your DNS server) 1. `http://pi.hole/admin/` (when using Pi-hole as your DNS server)
2. `http://<IP_ADDPRESS_OF_YOUR_PI_HOLE>/admin/` 2. `http://<IP_ADDPRESS_OF_YOUR_PI_HOLE>/admin/`
3. `http://pi.hole/` (when using Pi-hole as your DNS server)
## Faster-than-light Engine
FTLDNS is a lightweight, purpose-built daemon used to provide statistics needed for the Web Interface, and its API can be easily integrated into your own projects. As the name implies, FTLDNS does this all *very quickly*!
Some of the statistics you can integrate include:
- Total number of domains being blocked
- Total number of DNS queries today
- Total number of ads blocked today
- Percentage of ads blocked
- Unique domains
- Queries forwarded (to your chosen upstream DNS server)
- Queries cached
- Unique clients
The API can be accessed via [`telnet`](https://github.com/pi-hole/FTL), the Web (`admin/api.php`) and Command Line (`pihole -c -j`). You can find out [more details over here](https://discourse.pi-hole.net/t/pi-hole-api/1863).

View file

@ -39,6 +39,4 @@ cache-size=@CACHE_SIZE@
log-queries log-queries
log-facility=/var/log/pihole.log log-facility=/var/log/pihole.log
local-ttl=2
log-async log-async

41
advanced/06-rfc6761.conf Normal file
View file

@ -0,0 +1,41 @@
# Pi-hole: A black hole for Internet advertisements
# (c) 2021 Pi-hole, LLC (https://pi-hole.net)
# Network-wide ad blocking via your own hardware.
#
# RFC 6761 config file for Pi-hole
#
# This file is copyright under the latest version of the EUPL.
# Please see LICENSE file for your rights under this license.
###############################################################################
# FILE AUTOMATICALLY POPULATED BY PI-HOLE INSTALL/UPDATE PROCEDURE. #
# ANY CHANGES MADE TO THIS FILE AFTER INSTALL WILL BE LOST ON THE NEXT UPDATE #
# #
# CHANGES SHOULD BE MADE IN A SEPARATE CONFIG FILE #
# WITHIN /etc/dnsmasq.d/yourname.conf #
###############################################################################
# RFC 6761: Caching DNS servers SHOULD recognize
# test, localhost, invalid
# names as special and SHOULD NOT attempt to look up NS records for them, or
# otherwise query authoritative DNS servers in an attempt to resolve these
# names.
server=/test/
server=/localhost/
server=/invalid/
# The same RFC requests something similar for
# 16.172.in-addr.arpa. 22.172.in-addr.arpa. 27.172.in-addr.arpa.
# 17.172.in-addr.arpa. 30.172.in-addr.arpa. 28.172.in-addr.arpa.
# 18.172.in-addr.arpa. 23.172.in-addr.arpa. 29.172.in-addr.arpa.
# 19.172.in-addr.arpa. 24.172.in-addr.arpa. 31.172.in-addr.arpa.
# 20.172.in-addr.arpa. 25.172.in-addr.arpa. 168.192.in-addr.arpa.
# Pi-hole implements this via the dnsmasq option "bogus-priv" (see
# 01-pihole.conf) because this also covers IPv6.
# OpenWRT furthermore blocks bind, local, onion domains
# see https://git.openwrt.org/?p=openwrt/openwrt.git;a=blob_plain;f=package/network/services/dnsmasq/files/rfc6761.conf;hb=HEAD
# and https://www.iana.org/assignments/special-use-domain-names/special-use-domain-names.xhtml
# We do not include the ".local" rule ourselves, see https://github.com/pi-hole/pi-hole/pull/4282#discussion_r689112972
server=/bind/
server=/onion/

View file

@ -498,10 +498,6 @@ chronoFunc() {
printFunc " RAM usage: " "$ram_perc%" "$ram_info" printFunc " RAM usage: " "$ram_perc%" "$ram_info"
printFunc " HDD usage: " "$disk_perc" "$disk_info" printFunc " HDD usage: " "$disk_perc" "$disk_info"
if [[ "$scr_lines" -gt 17 ]] && [[ "$chrono_width" != "small" ]]; then
printFunc " LAN addr: " "${IPV4_ADDRESS/\/*/}" "$lan_info"
fi
if [[ "$DHCP_ACTIVE" == "true" ]]; then if [[ "$DHCP_ACTIVE" == "true" ]]; then
printFunc "DHCP usage: " "$ph_dhcp_percent%" "$dhcp_info" printFunc "DHCP usage: " "$ph_dhcp_percent%" "$dhcp_info"
fi fi
@ -559,7 +555,7 @@ Calculates stats and displays to an LCD
Options: Options:
-j, --json Output stats as JSON formatted string -j, --json Output stats as JSON formatted string
-r, --refresh Set update frequency (in seconds) -r, --refresh Set update frequency (in seconds)
-e, --exit Output stats and exit witout refreshing -e, --exit Output stats and exit without refreshing
-h, --help Display this help text" -h, --help Display this help text"
fi fi

14
advanced/Scripts/database_migration/gravity-db.sh Normal file → Executable file
View file

@ -111,9 +111,21 @@ upgrade_gravityDB(){
version=12 version=12
fi fi
if [[ "$version" == "12" ]]; then if [[ "$version" == "12" ]]; then
# Add column date_updated to alist table # Add column date_updated to adlist table
echo -e " ${INFO} Upgrading gravity database from version 12 to 13" echo -e " ${INFO} Upgrading gravity database from version 12 to 13"
sqlite3 "${database}" < "${scriptPath}/12_to_13.sql" sqlite3 "${database}" < "${scriptPath}/12_to_13.sql"
version=13 version=13
fi fi
if [[ "$version" == "13" ]]; then
# Add columns number and status to adlist table
echo -e " ${INFO} Upgrading gravity database from version 13 to 14"
sqlite3 "${database}" < "${scriptPath}/13_to_14.sql"
version=14
fi
if [[ "$version" == "14" ]]; then
# Changes the vw_adlist created in 5_to_6
echo -e " ${INFO} Upgrading gravity database from version 14 to 15"
sqlite3 "${database}" < "${scriptPath}/14_to_15.sql"
version=15
fi
} }

View file

@ -0,0 +1,13 @@
.timeout 30000
PRAGMA FOREIGN_KEYS=OFF;
BEGIN TRANSACTION;
ALTER TABLE adlist ADD COLUMN number INTEGER NOT NULL DEFAULT 0;
ALTER TABLE adlist ADD COLUMN invalid_domains INTEGER NOT NULL DEFAULT 0;
ALTER TABLE adlist ADD COLUMN status INTEGER NOT NULL DEFAULT 0;
UPDATE info SET value = 14 WHERE property = 'version';
COMMIT;

View file

@ -0,0 +1,15 @@
.timeout 30000
PRAGMA FOREIGN_KEYS=OFF;
BEGIN TRANSACTION;
DROP VIEW vw_adlist;
CREATE VIEW vw_adlist AS SELECT DISTINCT address, id
FROM adlist
WHERE enabled = 1
ORDER BY id;
UPDATE info SET value = 15 WHERE property = 'version';
COMMIT;

View file

@ -1,4 +1,6 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# shellcheck disable=SC1090
# Pi-hole: A black hole for Internet advertisements # Pi-hole: A black hole for Internet advertisements
# (c) 2017 Pi-hole, LLC (https://pi-hole.net) # (c) 2017 Pi-hole, LLC (https://pi-hole.net)
# Network-wide ad blocking via your own hardware. # Network-wide ad blocking via your own hardware.
@ -9,11 +11,19 @@
# Please see LICENSE file for your rights under this license. # Please see LICENSE file for your rights under this license.
# Globals # Globals
basename=pihole piholeDir="/etc/pihole"
piholeDir=/etc/"${basename}" GRAVITYDB="${piholeDir}/gravity.db"
gravityDBfile="${piholeDir}/gravity.db" # Source pihole-FTL from install script
pihole_FTL="${piholeDir}/pihole-FTL.conf"
if [[ -f "${pihole_FTL}" ]]; then
source "${pihole_FTL}"
fi
reload=false # Set this only after sourcing pihole-FTL.conf as the gravity database path may
# have changed
gravityDBfile="${GRAVITYDB}"
noReloadRequested=false
addmode=true addmode=true
verbose=true verbose=true
wildcard=false wildcard=false
@ -25,6 +35,7 @@ typeId=""
comment="" comment=""
declare -i domaincount declare -i domaincount
domaincount=0 domaincount=0
reload=false
colfile="/opt/pihole/COL_TABLE" colfile="/opt/pihole/COL_TABLE"
source ${colfile} source ${colfile}
@ -112,7 +123,7 @@ ProcessDomainList() {
for dom in "${domList[@]}"; do for dom in "${domList[@]}"; do
# Format domain into regex filter if requested # Format domain into regex filter if requested
if [[ "${wildcard}" == true ]]; then if [[ "${wildcard}" == true ]]; then
dom="(^|\\.)${dom//\./\\.}$" dom="(\\.|^)${dom//\./\\.}$"
fi fi
# Logic: If addmode then add to desired list and remove from the other; # Logic: If addmode then add to desired list and remove from the other;
@ -232,13 +243,13 @@ Displaylist() {
NukeList() { NukeList() {
count=$(sqlite3 "${gravityDBfile}" "SELECT COUNT(1) FROM domainlist WHERE type = ${typeId};") count=$(sqlite3 "${gravityDBfile}" "SELECT COUNT(1) FROM domainlist WHERE type = ${typeId};")
listname="$(GetListnameFromTypeId "${typeId}")" listname="$(GetListnameFromTypeId "${typeId}")"
if [ "$count" -gt 0 ];then if [ "$count" -gt 0 ];then
sqlite3 "${gravityDBfile}" "DELETE FROM domainlist WHERE type = ${typeId};" sqlite3 "${gravityDBfile}" "DELETE FROM domainlist WHERE type = ${typeId};"
echo " ${TICK} Removed ${count} domain(s) from the ${listname}" echo " ${TICK} Removed ${count} domain(s) from the ${listname}"
else else
echo " ${INFO} ${listname} already empty. Nothing to do!" echo " ${INFO} ${listname} already empty. Nothing to do!"
fi fi
exit 0; exit 0;
} }
@ -258,7 +269,7 @@ while (( "$#" )); do
"--white-wild" | "white-wild" ) typeId=2; wildcard=true;; "--white-wild" | "white-wild" ) typeId=2; wildcard=true;;
"--wild" | "wildcard" ) typeId=3; wildcard=true;; "--wild" | "wildcard" ) typeId=3; wildcard=true;;
"--regex" | "regex" ) typeId=3;; "--regex" | "regex" ) typeId=3;;
"-nr"| "--noreload" ) reload=false;; "-nr"| "--noreload" ) noReloadRequested=true;;
"-d" | "--delmode" ) addmode=false;; "-d" | "--delmode" ) addmode=false;;
"-q" | "--quiet" ) verbose=false;; "-q" | "--quiet" ) verbose=false;;
"-h" | "--help" ) helpFunc;; "-h" | "--help" ) helpFunc;;
@ -284,6 +295,6 @@ if $web; then
echo "DONE" echo "DONE"
fi fi
if [[ "${reload}" != false ]]; then if [[ ${reload} == true && ${noReloadRequested} == false ]]; then
pihole restartdns reload-lists pihole restartdns reload-lists
fi fi

View file

@ -38,7 +38,7 @@ flushARP(){
# Truncate network_addresses table in pihole-FTL.db # Truncate network_addresses table in pihole-FTL.db
# This needs to be done before we can truncate the network table due to # This needs to be done before we can truncate the network table due to
# foreign key contraints # foreign key constraints
if ! output=$(sqlite3 "${DBFILE}" "DELETE FROM network_addresses" 2>&1); then if ! output=$(sqlite3 "${DBFILE}" "DELETE FROM network_addresses" 2>&1); then
echo -e "${OVER} ${CROSS} Failed to truncate network_addresses table" echo -e "${OVER} ${CROSS} Failed to truncate network_addresses table"
echo " Database location: ${DBFILE}" echo " Database location: ${DBFILE}"

3
advanced/Scripts/piholeCheckout.sh Normal file → Executable file
View file

@ -166,12 +166,15 @@ checkout() {
checkout_pull_branch "${webInterfaceDir}" "${2}" checkout_pull_branch "${webInterfaceDir}" "${2}"
elif [[ "${1}" == "ftl" ]] ; then elif [[ "${1}" == "ftl" ]] ; then
local path local path
local oldbranch
path="${2}/${binary}" path="${2}/${binary}"
oldbranch="$(pihole-FTL -b)"
if check_download_exists "$path"; then if check_download_exists "$path"; then
echo " ${TICK} Branch ${2} exists" echo " ${TICK} Branch ${2} exists"
echo "${2}" > /etc/pihole/ftlbranch echo "${2}" > /etc/pihole/ftlbranch
chmod 644 /etc/pihole/ftlbranch chmod 644 /etc/pihole/ftlbranch
echo -e " ${INFO} Switching to branch: \"${2}\" from \"${oldbranch}\""
FTLinstall "${binary}" FTLinstall "${binary}"
restart_service pihole-FTL restart_service pihole-FTL
enable_service pihole-FTL enable_service pihole-FTL

View file

@ -56,11 +56,6 @@ FAQ_BAD_ADDRESS="${COL_CYAN}https://discourse.pi-hole.net/t/why-do-i-see-bad-add
# Other URLs we may use # Other URLs we may use
FORUMS_URL="${COL_CYAN}https://discourse.pi-hole.net${COL_NC}" FORUMS_URL="${COL_CYAN}https://discourse.pi-hole.net${COL_NC}"
TRICORDER_CONTEST="${COL_CYAN}https://pi-hole.net/2016/11/07/crack-our-medical-tricorder-win-a-raspberry-pi-3/${COL_NC}"
# Port numbers used for uploading the debug log
TRICORDER_NC_PORT_NUMBER=9999
TRICORDER_SSL_PORT_NUMBER=9998
# Directories required by Pi-hole # Directories required by Pi-hole
# https://discourse.pi-hole.net/t/what-files-does-pi-hole-use/1684 # https://discourse.pi-hole.net/t/what-files-does-pi-hole-use/1684
@ -78,15 +73,12 @@ HTML_DIRECTORY="/var/www/html"
WEB_GIT_DIRECTORY="${HTML_DIRECTORY}/admin" WEB_GIT_DIRECTORY="${HTML_DIRECTORY}/admin"
#BLOCK_PAGE_DIRECTORY="${HTML_DIRECTORY}/pihole" #BLOCK_PAGE_DIRECTORY="${HTML_DIRECTORY}/pihole"
SHM_DIRECTORY="/dev/shm" SHM_DIRECTORY="/dev/shm"
ETC="/etc"
# Files required by Pi-hole # Files required by Pi-hole
# https://discourse.pi-hole.net/t/what-files-does-pi-hole-use/1684 # https://discourse.pi-hole.net/t/what-files-does-pi-hole-use/1684
PIHOLE_CRON_FILE="${CRON_D_DIRECTORY}/pihole" PIHOLE_CRON_FILE="${CRON_D_DIRECTORY}/pihole"
PIHOLE_DNS_CONFIG_FILE="${DNSMASQ_D_DIRECTORY}/01-pihole.conf"
PIHOLE_DHCP_CONFIG_FILE="${DNSMASQ_D_DIRECTORY}/02-pihole-dhcp.conf"
PIHOLE_WILDCARD_CONFIG_FILE="${DNSMASQ_D_DIRECTORY}/03-wildcard.conf"
WEB_SERVER_CONFIG_FILE="${WEB_SERVER_CONFIG_DIRECTORY}/lighttpd.conf" WEB_SERVER_CONFIG_FILE="${WEB_SERVER_CONFIG_DIRECTORY}/lighttpd.conf"
WEB_SERVER_CUSTOM_CONFIG_FILE="${WEB_SERVER_CONFIG_DIRECTORY}/external.conf" WEB_SERVER_CUSTOM_CONFIG_FILE="${WEB_SERVER_CONFIG_DIRECTORY}/external.conf"
@ -141,6 +133,9 @@ PIHOLE_FTL_LOG="$(get_ftl_conf_value "LOGFILE" "${LOG_DIRECTORY}/pihole-FTL.log"
PIHOLE_WEB_SERVER_ACCESS_LOG_FILE="${WEB_SERVER_LOG_DIRECTORY}/access.log" PIHOLE_WEB_SERVER_ACCESS_LOG_FILE="${WEB_SERVER_LOG_DIRECTORY}/access.log"
PIHOLE_WEB_SERVER_ERROR_LOG_FILE="${WEB_SERVER_LOG_DIRECTORY}/error.log" PIHOLE_WEB_SERVER_ERROR_LOG_FILE="${WEB_SERVER_LOG_DIRECTORY}/error.log"
RESOLVCONF="${ETC}/resolv.conf"
DNSMASQ_CONF="${ETC}/dnsmasq.conf"
# An array of operating system "pretty names" that we officially support # An array of operating system "pretty names" that we officially support
# We can loop through the array at any time to see if it matches a value # We can loop through the array at any time to see if it matches a value
#SUPPORTED_OS=("Raspbian" "Ubuntu" "Fedora" "Debian" "CentOS") #SUPPORTED_OS=("Raspbian" "Ubuntu" "Fedora" "Debian" "CentOS")
@ -165,9 +160,6 @@ PIHOLE_PROCESSES=( "lighttpd" "pihole-FTL" )
# Store the required directories in an array so it can be parsed through # Store the required directories in an array so it can be parsed through
REQUIRED_FILES=("${PIHOLE_CRON_FILE}" REQUIRED_FILES=("${PIHOLE_CRON_FILE}"
"${PIHOLE_DNS_CONFIG_FILE}"
"${PIHOLE_DHCP_CONFIG_FILE}"
"${PIHOLE_WILDCARD_CONFIG_FILE}"
"${WEB_SERVER_CONFIG_FILE}" "${WEB_SERVER_CONFIG_FILE}"
"${WEB_SERVER_CUSTOM_CONFIG_FILE}" "${WEB_SERVER_CUSTOM_CONFIG_FILE}"
"${PIHOLE_INSTALL_LOG_FILE}" "${PIHOLE_INSTALL_LOG_FILE}"
@ -185,7 +177,9 @@ REQUIRED_FILES=("${PIHOLE_CRON_FILE}"
"${PIHOLE_DEBUG_LOG}" "${PIHOLE_DEBUG_LOG}"
"${PIHOLE_FTL_LOG}" "${PIHOLE_FTL_LOG}"
"${PIHOLE_WEB_SERVER_ACCESS_LOG_FILE}" "${PIHOLE_WEB_SERVER_ACCESS_LOG_FILE}"
"${PIHOLE_WEB_SERVER_ERROR_LOG_FILE}") "${PIHOLE_WEB_SERVER_ERROR_LOG_FILE}"
"${RESOLVCONF}"
"${DNSMASQ_CONF}")
DISCLAIMER="This process collects information from your Pi-hole, and optionally uploads it to a unique and random directory on tricorder.pi-hole.net. DISCLAIMER="This process collects information from your Pi-hole, and optionally uploads it to a unique and random directory on tricorder.pi-hole.net.
@ -235,6 +229,7 @@ copy_to_debug_log() {
} }
initialize_debug() { initialize_debug() {
local system_uptime
# Clear the screen so the debug log is readable # Clear the screen so the debug log is readable
clear clear
show_disclaimer show_disclaimer
@ -242,9 +237,13 @@ initialize_debug() {
log_write "${COL_PURPLE}*** [ INITIALIZING ]${COL_NC}" log_write "${COL_PURPLE}*** [ INITIALIZING ]${COL_NC}"
# Timestamp the start of the log # Timestamp the start of the log
log_write "${INFO} $(date "+%Y-%m-%d:%H:%M:%S") debug log has been initialized." log_write "${INFO} $(date "+%Y-%m-%d:%H:%M:%S") debug log has been initialized."
# Uptime of the system
# credits to https://stackoverflow.com/questions/28353409/bash-format-uptime-to-show-days-hours-minutes
system_uptime=$(uptime | awk -F'( |,|:)+' '{if ($7=="min") m=$6; else {if ($7~/^day/){if ($9=="min") {d=$6;m=$8} else {d=$6;h=$8;m=$9}} else {h=$6;m=$7}}} {print d+0,"days,",h+0,"hours,",m+0,"minutes"}')
log_write "${INFO} System has been running for ${system_uptime}"
} }
# This is a function for visually displaying the curent test that is being run. # This is a function for visually displaying the current test that is being run.
# Accepts one variable: the name of what is being diagnosed # Accepts one variable: the name of what is being diagnosed
# Colors do not show in the dasboard, but the icons do: [i], [✓], and [✗] # Colors do not show in the dasboard, but the icons do: [i], [✓], and [✗]
echo_current_diagnostic() { echo_current_diagnostic() {
@ -379,7 +378,7 @@ get_program_version() {
# Create a local variable so this function can be safely reused # Create a local variable so this function can be safely reused
local program_version local program_version
echo_current_diagnostic "${program_name} version" echo_current_diagnostic "${program_name} version"
# Evalutate the program we are checking, if it is any of the ones below, show the version # Evaluate the program we are checking, if it is any of the ones below, show the version
case "${program_name}" in case "${program_name}" in
"lighttpd") program_version="$(${program_name} -v 2> /dev/null | head -n1 | cut -d '/' -f2 | cut -d ' ' -f1)" "lighttpd") program_version="$(${program_name} -v 2> /dev/null | head -n1 | cut -d '/' -f2 | cut -d ' ' -f1)"
;; ;;
@ -410,12 +409,12 @@ os_check() {
# This function gets a list of supported OS versions from a TXT record at versions.pi-hole.net # This function gets a list of supported OS versions from a TXT record at versions.pi-hole.net
# and determines whether or not the script is running on one of those systems # and determines whether or not the script is running on one of those systems
local remote_os_domain valid_os valid_version detected_os detected_version cmdResult digReturnCode response local remote_os_domain valid_os valid_version detected_os detected_version cmdResult digReturnCode response
remote_os_domain="versions.pi-hole.net" remote_os_domain=${OS_CHECK_DOMAIN_NAME:-"versions.pi-hole.net"}
detected_os=$(grep "\bID\b" /etc/os-release | cut -d '=' -f2 | tr -d '"') detected_os=$(grep "\bID\b" /etc/os-release | cut -d '=' -f2 | tr -d '"')
detected_version=$(grep VERSION_ID /etc/os-release | cut -d '=' -f2 | tr -d '"') detected_version=$(grep VERSION_ID /etc/os-release | cut -d '=' -f2 | tr -d '"')
cmdResult="$(dig +short -t txt ${remote_os_domain} @ns1.pi-hole.net 2>&1; echo $?)" cmdResult="$(dig +short -t txt "${remote_os_domain}" @ns1.pi-hole.net 2>&1; echo $?)"
#Get the return code of the previous command (last line) #Get the return code of the previous command (last line)
digReturnCode="${cmdResult##*$'\n'}" digReturnCode="${cmdResult##*$'\n'}"
@ -586,6 +585,27 @@ processor_check() {
fi fi
} }
disk_usage() {
local file_system
local hide
echo_current_diagnostic "Disk usage"
mapfile -t file_system < <(df -h)
# Some lines of df might contain sensitive information like usernames and passwords.
# E.g. curlftpfs filesystems (https://www.looklinux.com/mount-ftp-share-on-linux-using-curlftps/)
# We are not interested in those lines so we collect keyword, to remove them from the output
# Additinal keywords can be added, separated by "|"
hide="curlftpfs"
# only show those lines not containg a sensitive phrase
for line in "${file_system[@]}"; do
if [[ ! $line =~ $hide ]]; then
log_write " ${line}"
fi
done
}
parse_setup_vars() { parse_setup_vars() {
echo_current_diagnostic "Setup variables" echo_current_diagnostic "Setup variables"
# If the file exists, # If the file exists,
@ -605,43 +625,11 @@ parse_locale() {
parse_file "${pihole_locale}" parse_file "${pihole_locale}"
} }
does_ip_match_setup_vars() {
# Check for IPv4 or 6
local protocol="${1}"
# IP address to check for
local ip_address="${2}"
# See what IP is in the setupVars.conf file
local setup_vars_ip
setup_vars_ip=$(< ${PIHOLE_SETUP_VARS_FILE} grep IPV"${protocol}"_ADDRESS | cut -d '=' -f2)
# If it's an IPv6 address
if [[ "${protocol}" == "6" ]]; then
# Strip off the / (CIDR notation)
if [[ "${ip_address%/*}" == "${setup_vars_ip%/*}" ]]; then
# if it matches, show it in green
log_write " ${COL_GREEN}${ip_address%/*}${COL_NC} matches the IP found in ${PIHOLE_SETUP_VARS_FILE}"
else
# otherwise show it in red with an FAQ URL
log_write " ${COL_RED}${ip_address%/*}${COL_NC} does not match the IP found in ${PIHOLE_SETUP_VARS_FILE} (${FAQ_ULA})"
fi
else
# if the protocol isn't 6, it's 4 so no need to strip the CIDR notation
# since it exists in the setupVars.conf that way
if [[ "${ip_address}" == "${setup_vars_ip}" ]]; then
# show in green if it matches
log_write " ${COL_GREEN}${ip_address}${COL_NC} matches the IP found in ${PIHOLE_SETUP_VARS_FILE}"
else
# otherwise show it in red
log_write " ${COL_RED}${ip_address}${COL_NC} does not match the IP found in ${PIHOLE_SETUP_VARS_FILE} (${FAQ_ULA})"
fi
fi
}
detect_ip_addresses() { detect_ip_addresses() {
# First argument should be a 4 or a 6 # First argument should be a 4 or a 6
local protocol=${1} local protocol=${1}
# Use ip to show the addresses for the chosen protocol # Use ip to show the addresses for the chosen protocol
# Store the values in an arry so they can be looped through # Store the values in an array so they can be looped through
# Get the lines that are in the file(s) and store them in an array for parsing later # Get the lines that are in the file(s) and store them in an array for parsing later
mapfile -t ip_addr_list < <(ip -"${protocol}" addr show dev "${PIHOLE_INTERFACE}" | awk -F ' ' '{ for(i=1;i<=NF;i++) if ($i ~ '/^inet/') print $(i+1) }') mapfile -t ip_addr_list < <(ip -"${protocol}" addr show dev "${PIHOLE_INTERFACE}" | awk -F ' ' '{ for(i=1;i<=NF;i++) if ($i ~ '/^inet/') print $(i+1) }')
@ -653,8 +641,7 @@ detect_ip_addresses() {
log_write "${TICK} IPv${protocol} address(es) bound to the ${PIHOLE_INTERFACE} interface:" log_write "${TICK} IPv${protocol} address(es) bound to the ${PIHOLE_INTERFACE} interface:"
# Since there may be more than one IP address, store them in an array # Since there may be more than one IP address, store them in an array
for i in "${!ip_addr_list[@]}"; do for i in "${!ip_addr_list[@]}"; do
# For each one in the list, print it out log_write " ${ip_addr_list[$i]}"
does_ip_match_setup_vars "${protocol}" "${ip_addr_list[$i]}"
done done
# Print a blank line just for formatting # Print a blank line just for formatting
log_write "" log_write ""
@ -663,13 +650,6 @@ detect_ip_addresses() {
log_write "${CROSS} ${COL_RED}No IPv${protocol} address(es) found on the ${PIHOLE_INTERFACE}${COL_NC} interface.\\n" log_write "${CROSS} ${COL_RED}No IPv${protocol} address(es) found on the ${PIHOLE_INTERFACE}${COL_NC} interface.\\n"
return 1 return 1
fi fi
# If the protocol is v6
if [[ "${protocol}" == "6" ]]; then
# let the user know that as long as there is one green address, things should be ok
log_write " ^ Please note that you may have more than one IP address listed."
log_write " As long as one of them is green, and it matches what is in ${PIHOLE_SETUP_VARS_FILE}, there is no need for concern.\\n"
log_write " The link to the FAQ is for an issue that sometimes occurs when the IPv6 address changes, which is why we check for it.\\n"
fi
} }
ping_ipv4_or_ipv6() { ping_ipv4_or_ipv6() {
@ -695,7 +675,7 @@ ping_gateway() {
# Check if we are using IPv4 or IPv6 # Check if we are using IPv4 or IPv6
# Find the default gateway using IPv4 or IPv6 # Find the default gateway using IPv4 or IPv6
local gateway local gateway
gateway="$(ip -"${protocol}" route | grep default | cut -d ' ' -f 3)" gateway="$(ip -"${protocol}" route | grep default | grep "${PIHOLE_INTERFACE}" | cut -d ' ' -f 3)"
# If the gateway variable has a value (meaning a gateway was found), # If the gateway variable has a value (meaning a gateway was found),
if [[ -n "${gateway}" ]]; then if [[ -n "${gateway}" ]]; then
@ -823,7 +803,7 @@ check_x_headers() {
# Do it for the dashboard as well, as the header is different than above # Do it for the dashboard as well, as the header is different than above
local dashboard local dashboard
dashboard=$(curl -Is localhost/admin/ | awk '/X-Pi-hole/' | tr -d '\r') dashboard=$(curl -Is localhost/admin/ | awk '/X-Pi-hole/' | tr -d '\r')
# Store what the X-Header shoud be in variables for comparison later # Store what the X-Header should be in variables for comparison later
local block_page_working local block_page_working
block_page_working="X-Pi-hole: A black hole for Internet advertisements." block_page_working="X-Pi-hole: A black hole for Internet advertisements."
local dashboard_working local dashboard_working
@ -842,12 +822,12 @@ check_x_headers() {
log_write "${COL_RED}${full_curl_output_block_page}${COL_NC}" log_write "${COL_RED}${full_curl_output_block_page}${COL_NC}"
fi fi
# Same logic applies to the dashbord as above, if the X-Header matches what a working system shoud have, # Same logic applies to the dashboard as above, if the X-Header matches what a working system should have,
if [[ $dashboard == "$dashboard_working" ]]; then if [[ $dashboard == "$dashboard_working" ]]; then
# then we can show a success # then we can show a success
log_write "$TICK Web interface X-Header: ${COL_GREEN}${dashboard}${COL_NC}" log_write "$TICK Web interface X-Header: ${COL_GREEN}${dashboard}${COL_NC}"
else else
# Othewise, it's a failure since the X-Headers either don't exist or have been modified in some way # Otherwise, it's a failure since the X-Headers either don't exist or have been modified in some way
log_write "$CROSS Web interface X-Header: ${COL_RED}X-Header does not match or could not be retrieved.${COL_NC}" log_write "$CROSS Web interface X-Header: ${COL_RED}X-Header does not match or could not be retrieved.${COL_NC}"
log_write "${COL_RED}${full_curl_output_dashboard}${COL_NC}" log_write "${COL_RED}${full_curl_output_dashboard}${COL_NC}"
fi fi
@ -859,13 +839,13 @@ dig_at() {
# Store the arguments as variables with names # Store the arguments as variables with names
local protocol="${1}" local protocol="${1}"
local IP="${2}"
echo_current_diagnostic "Name resolution (IPv${protocol}) using a random blocked domain and a known ad-serving domain" echo_current_diagnostic "Name resolution (IPv${protocol}) using a random blocked domain and a known ad-serving domain"
# Set more local variables # Set more local variables
# We need to test name resolution locally, via Pi-hole, and via a public resolver # We need to test name resolution locally, via Pi-hole, and via a public resolver
local local_dig local local_dig
local pihole_dig
local remote_dig local remote_dig
local interfaces
local addresses
# Use a static domain that we know has IPv4 and IPv6 to avoid false positives # Use a static domain that we know has IPv4 and IPv6 to avoid false positives
# Sometimes the randomly chosen domains don't use IPv6, or something else is wrong with them # Sometimes the randomly chosen domains don't use IPv6, or something else is wrong with them
local remote_url="doubleclick.com" local remote_url="doubleclick.com"
@ -874,15 +854,15 @@ dig_at() {
if [[ ${protocol} == "6" ]]; then if [[ ${protocol} == "6" ]]; then
# Set the IPv6 variables and record type # Set the IPv6 variables and record type
local local_address="::1" local local_address="::1"
local pihole_address="${IP}"
local remote_address="2001:4860:4860::8888" local remote_address="2001:4860:4860::8888"
local sed_selector="inet6"
local record_type="AAAA" local record_type="AAAA"
# Othwerwise, it should be 4 # Otherwise, it should be 4
else else
# so use the IPv4 values # so use the IPv4 values
local local_address="127.0.0.1" local local_address="127.0.0.1"
local pihole_address="${IP}"
local remote_address="8.8.8.8" local remote_address="8.8.8.8"
local sed_selector="inet"
local record_type="A" local record_type="A"
fi fi
@ -892,32 +872,55 @@ dig_at() {
local random_url local random_url
random_url=$(sqlite3 "${PIHOLE_GRAVITY_DB_FILE}" "SELECT domain FROM vw_gravity ORDER BY RANDOM() LIMIT 1") random_url=$(sqlite3 "${PIHOLE_GRAVITY_DB_FILE}" "SELECT domain FROM vw_gravity ORDER BY RANDOM() LIMIT 1")
# First, do a dig on localhost to see if Pi-hole can use itself to block a domain
if local_dig=$(dig +tries=1 +time=2 -"${protocol}" "${random_url}" @${local_address} +short "${record_type}"); then
# If it can, show success
log_write "${TICK} ${random_url} ${COL_GREEN}is ${local_dig}${COL_NC} via ${COL_CYAN}localhost$COL_NC (${local_address})"
else
# Otherwise, show a failure
log_write "${CROSS} ${COL_RED}Failed to resolve${COL_NC} ${random_url} via ${COL_RED}localhost${COL_NC} (${local_address})"
fi
# Next we need to check if Pi-hole can resolve a domain when the query is sent to it's IP address # Next we need to check if Pi-hole can resolve a domain when the query is sent to it's IP address
# This better emulates how clients will interact with Pi-hole as opposed to above where Pi-hole is # This better emulates how clients will interact with Pi-hole as opposed to above where Pi-hole is
# just asing itself locally # just asing itself locally
# The default timeouts and tries are reduced in case the DNS server isn't working, so the user isn't waiting for too long # The default timeouts and tries are reduced in case the DNS server isn't working, so the user isn't
# waiting for too long
#
# Turn off history expansion such that the "!" in the sed command cannot do silly things
set +H
# Get interfaces
# sed logic breakdown:
# / master /d;
# Removes all interfaces that are slaves of others (e.g. virtual docker interfaces)
# /UP/!d;
# Removes all interfaces which are not UP
# s/^[0-9]*: //g;
# Removes interface index
# s/: <.*//g;
# Removes everything after the interface name
interfaces="$(ip link show | sed "/ master /d;/UP/!d;s/^[0-9]*: //g;s/: <.*//g;")"
# If Pi-hole can dig itself from it's IP (not the loopback address) while IFS= read -r iface ; do
if pihole_dig=$(dig +tries=1 +time=2 -"${protocol}" "${random_url}" @"${pihole_address}" +short "${record_type}"); then # Get addresses of current interface
# show a success # sed logic breakdown:
log_write "${TICK} ${random_url} ${COL_GREEN}is ${pihole_dig}${COL_NC} via ${COL_CYAN}Pi-hole${COL_NC} (${pihole_address})" # /inet(|6) /!d;
else # Removes all lines from ip a that do not contain either "inet " or "inet6 "
# Othewise, show a failure # s/^.*inet(|6) //g;
log_write "${CROSS} ${COL_RED}Failed to resolve${COL_NC} ${random_url} via ${COL_RED}Pi-hole${COL_NC} (${pihole_address})" # Removes all leading whitespace as well as the "inet " or "inet6 " string
fi # s/\/.*$//g;
# Removes CIDR and everything thereafter (e.g., scope properties)
addresses="$(ip address show dev "${iface}" | sed "/${sed_selector} /!d;s/^.*${sed_selector} //g;s/\/.*$//g;")"
if [ -n "${addresses}" ]; then
while IFS= read -r local_address ; do
# Check if Pi-hole can use itself to block a domain
if local_dig=$(dig +tries=1 +time=2 -"${protocol}" "${random_url}" @"${local_address}" +short "${record_type}"); then
# If it can, show success
log_write "${TICK} ${random_url} ${COL_GREEN}is ${local_dig}${COL_NC} on ${COL_CYAN}${iface}${COL_NC} (${COL_CYAN}${local_address}${COL_NC})"
else
# Otherwise, show a failure
log_write "${CROSS} ${COL_RED}Failed to resolve${COL_NC} ${random_url} on ${COL_RED}${iface}${COL_NC} (${COL_RED}${local_address}${COL_NC})"
fi
done <<< "${addresses}"
else
log_write "${TICK} No IPv${protocol} address available on ${COL_CYAN}${iface}${COL_NC}"
fi
done <<< "${interfaces}"
# Finally, we need to make sure legitimate queries can out to the Internet using an external, public DNS server # Finally, we need to make sure legitimate queries can out to the Internet using an external, public DNS server
# We are using the static remote_url here instead of a random one because we know it works with IPv4 and IPv6 # We are using the static remote_url here instead of a random one because we know it works with IPv4 and IPv6
if remote_dig=$(dig +tries=1 +time=2 -"${protocol}" "${remote_url}" @${remote_address} +short "${record_type}" | head -n1); then if remote_dig=$(dig +tries=1 +time=2 -"${protocol}" "${remote_url}" @"${remote_address}" +short "${record_type}" | head -n1); then
# If successful, the real IP of the domain will be returned instead of Pi-hole's IP # If successful, the real IP of the domain will be returned instead of Pi-hole's IP
log_write "${TICK} ${remote_url} ${COL_GREEN}is ${remote_dig}${COL_NC} via ${COL_CYAN}a remote, public DNS server${COL_NC} (${remote_address})" log_write "${TICK} ${remote_url} ${COL_GREEN}is ${remote_dig}${COL_NC} via ${COL_CYAN}a remote, public DNS server${COL_NC} (${remote_address})"
else else
@ -1032,7 +1035,7 @@ parse_file() {
local file_lines local file_lines
# For each line in the file, # For each line in the file,
for file_lines in "${file_info[@]}"; do for file_lines in "${file_info[@]}"; do
if [[ ! -z "${file_lines}" ]]; then if [[ -n "${file_lines}" ]]; then
# don't include the Web password hash # don't include the Web password hash
[[ "${file_lines}" =~ ^\#.*$ || ! "${file_lines}" || "${file_lines}" == "WEBPASSWORD="* ]] && continue [[ "${file_lines}" =~ ^\#.*$ || ! "${file_lines}" || "${file_lines}" == "WEBPASSWORD="* ]] && continue
# otherwise, display the lines of the file # otherwise, display the lines of the file
@ -1044,14 +1047,10 @@ parse_file() {
} }
check_name_resolution() { check_name_resolution() {
# Check name resoltion from localhost, Pi-hole's IP, and Google's name severs # Check name resolution from localhost, Pi-hole's IP, and Google's name severs
# using the function we created earlier # using the function we created earlier
dig_at 4 "${IPV4_ADDRESS%/*}" dig_at 4
# If IPv6 enabled, dig_at 6
if [[ "${IPV6_ADDRESS}" ]]; then
# check resolution
dig_at 6 "${IPV6_ADDRESS%/*}"
fi
} }
# This function can check a directory exists # This function can check a directory exists
@ -1094,13 +1093,17 @@ list_files_in_dir() {
: :
elif [[ "${dir_to_parse}" == "${SHM_DIRECTORY}" ]]; then elif [[ "${dir_to_parse}" == "${SHM_DIRECTORY}" ]]; then
# SHM file - we do not want to see the content, but we want to see the files and their sizes # SHM file - we do not want to see the content, but we want to see the files and their sizes
log_write "$(ls -ld "${dir_to_parse}"/"${each_file}")" log_write "$(ls -lhd "${dir_to_parse}"/"${each_file}")"
elif [[ "${dir_to_parse}" == "${DNSMASQ_D_DIRECTORY}" ]]; then
# in case of the dnsmasq directory inlcuede all files in the debug output
log_write "\\n${COL_GREEN}$(ls -lhd "${dir_to_parse}"/"${each_file}")${COL_NC}"
make_array_from_file "${dir_to_parse}/${each_file}"
else else
# Then, parse the file's content into an array so each line can be analyzed if need be # Then, parse the file's content into an array so each line can be analyzed if need be
for i in "${!REQUIRED_FILES[@]}"; do for i in "${!REQUIRED_FILES[@]}"; do
if [[ "${dir_to_parse}/${each_file}" == "${REQUIRED_FILES[$i]}" ]]; then if [[ "${dir_to_parse}/${each_file}" == "${REQUIRED_FILES[$i]}" ]]; then
# display the filename # display the filename
log_write "\\n${COL_GREEN}$(ls -ld "${dir_to_parse}"/"${each_file}")${COL_NC}" log_write "\\n${COL_GREEN}$(ls -lhd "${dir_to_parse}"/"${each_file}")${COL_NC}"
# Check if the file we want to view has a limit (because sometimes we just need a little bit of info from the file, not the entire thing) # Check if the file we want to view has a limit (because sometimes we just need a little bit of info from the file, not the entire thing)
case "${dir_to_parse}/${each_file}" in case "${dir_to_parse}/${each_file}" in
# If it's Web server error log, give the first and last 25 lines # If it's Web server error log, give the first and last 25 lines
@ -1139,6 +1142,7 @@ show_content_of_pihole_files() {
show_content_of_files_in_dir "${WEB_SERVER_LOG_DIRECTORY}" show_content_of_files_in_dir "${WEB_SERVER_LOG_DIRECTORY}"
show_content_of_files_in_dir "${LOG_DIRECTORY}" show_content_of_files_in_dir "${LOG_DIRECTORY}"
show_content_of_files_in_dir "${SHM_DIRECTORY}" show_content_of_files_in_dir "${SHM_DIRECTORY}"
show_content_of_files_in_dir "${ETC}"
} }
head_tail_log() { head_tail_log() {
@ -1239,11 +1243,11 @@ show_groups() {
} }
show_adlists() { show_adlists() {
show_db_entries "Adlists" "SELECT id,CASE enabled WHEN '0' THEN ' 0' WHEN '1' THEN ' 1' ELSE enabled END enabled,GROUP_CONCAT(adlist_by_group.group_id) group_ids,address,datetime(date_added,'unixepoch','localtime') date_added,datetime(date_modified,'unixepoch','localtime') date_modified,comment FROM adlist LEFT JOIN adlist_by_group ON adlist.id = adlist_by_group.adlist_id GROUP BY id;" "4 7 12 100 19 19 50" show_db_entries "Adlists" "SELECT id,CASE enabled WHEN '0' THEN ' 0' WHEN '1' THEN ' 1' ELSE enabled END enabled,GROUP_CONCAT(adlist_by_group.group_id) group_ids,address,datetime(date_added,'unixepoch','localtime') date_added,datetime(date_modified,'unixepoch','localtime') date_modified,comment FROM adlist LEFT JOIN adlist_by_group ON adlist.id = adlist_by_group.adlist_id GROUP BY id;" "5 7 12 100 19 19 50"
} }
show_domainlist() { show_domainlist() {
show_db_entries "Domainlist (0/1 = exact white-/blacklist, 2/3 = regex white-/blacklist)" "SELECT id,CASE type WHEN '0' THEN '0 ' WHEN '1' THEN ' 1 ' WHEN '2' THEN ' 2 ' WHEN '3' THEN ' 3' ELSE type END type,CASE enabled WHEN '0' THEN ' 0' WHEN '1' THEN ' 1' ELSE enabled END enabled,GROUP_CONCAT(domainlist_by_group.group_id) group_ids,domain,datetime(date_added,'unixepoch','localtime') date_added,datetime(date_modified,'unixepoch','localtime') date_modified,comment FROM domainlist LEFT JOIN domainlist_by_group ON domainlist.id = domainlist_by_group.domainlist_id GROUP BY id;" "4 4 7 12 100 19 19 50" show_db_entries "Domainlist (0/1 = exact white-/blacklist, 2/3 = regex white-/blacklist)" "SELECT id,CASE type WHEN '0' THEN '0 ' WHEN '1' THEN ' 1 ' WHEN '2' THEN ' 2 ' WHEN '3' THEN ' 3' ELSE type END type,CASE enabled WHEN '0' THEN ' 0' WHEN '1' THEN ' 1' ELSE enabled END enabled,GROUP_CONCAT(domainlist_by_group.group_id) group_ids,domain,datetime(date_added,'unixepoch','localtime') date_added,datetime(date_modified,'unixepoch','localtime') date_modified,comment FROM domainlist LEFT JOIN domainlist_by_group ON domainlist.id = domainlist_by_group.domainlist_id GROUP BY id;" "5 4 7 12 100 19 19 50"
} }
show_clients() { show_clients() {
@ -1255,10 +1259,10 @@ show_messages() {
} }
analyze_gravity_list() { analyze_gravity_list() {
echo_current_diagnostic "Gravity List and Database" echo_current_diagnostic "Gravity Database"
local gravity_permissions local gravity_permissions
gravity_permissions=$(ls -ld "${PIHOLE_GRAVITY_DB_FILE}") gravity_permissions=$(ls -lhd "${PIHOLE_GRAVITY_DB_FILE}")
log_write "${COL_GREEN}${gravity_permissions}${COL_NC}" log_write "${COL_GREEN}${gravity_permissions}${COL_NC}"
show_db_entries "Info table" "SELECT property,value FROM info" "20 40" show_db_entries "Info table" "SELECT property,value FROM info" "20 40"
@ -1281,77 +1285,88 @@ analyze_gravity_list() {
IFS="$OLD_IFS" IFS="$OLD_IFS"
} }
analyze_pihole_log() { obfuscated_pihole_log() {
echo_current_diagnostic "Pi-hole log" local pihole_log=("$@")
local head_line local line
# Put the current Internal Field Separator into another variable so it can be restored later local error_to_check_for
OLD_IFS="$IFS" local line_to_obfuscate
# Get the lines that are in the file(s) and store them in an array for parsing later local obfuscated_line
IFS=$'\r\n' for line in "${pihole_log[@]}"; do
local pihole_log_permissions # A common error in the pihole.log is when there is a non-hosts formatted file
pihole_log_permissions=$(ls -ld "${PIHOLE_LOG}") # that the DNS server is attempting to read. Since it's not formatted
log_write "${COL_GREEN}${pihole_log_permissions}${COL_NC}" # correctly, there will be an entry for "bad address at line n"
local pihole_log_head=() # So we can check for that here and highlight it in red so the user can see it easily
mapfile -t pihole_log_head < <(head -n 20 ${PIHOLE_LOG}) error_to_check_for=$(echo "${line}" | grep 'bad address at')
log_write " ${COL_CYAN}-----head of $(basename ${PIHOLE_LOG})------${COL_NC}" # Some users may not want to have the domains they visit sent to us
local error_to_check_for # To that end, we check for lines in the log that would contain a domain name
local line_to_obfuscate line_to_obfuscate=$(echo "${line}" | grep ': query\|: forwarded\|: reply')
local obfuscated_line # If the variable contains a value, it found an error in the log
for head_line in "${pihole_log_head[@]}"; do if [[ -n ${error_to_check_for} ]]; then
# A common error in the pihole.log is when there is a non-hosts formatted file # So we can print it in red to make it visible to the user
# that the DNS server is attempting to read. Since it's not formatted log_write " ${CROSS} ${COL_RED}${line}${COL_NC} (${FAQ_BAD_ADDRESS})"
# correctly, there will be an entry for "bad address at line n" else
# So we can check for that here and highlight it in red so the user can see it easily # If the variable does not a value (the current default behavior), so do not obfuscate anything
error_to_check_for=$(echo "${head_line}" | grep 'bad address at') if [[ -z ${OBFUSCATE} ]]; then
# Some users may not want to have the domains they visit sent to us log_write " ${line}"
# To that end, we check for lines in the log that would contain a domain name # Othwerise, a flag was passed to this command to obfuscate domains in the log
line_to_obfuscate=$(echo "${head_line}" | grep ': query\|: forwarded\|: reply') else
# If the variable contains a value, it found an error in the log # So first check if there are domains in the log that should be obfuscated
if [[ -n ${error_to_check_for} ]]; then if [[ -n ${line_to_obfuscate} ]]; then
# So we can print it in red to make it visible to the user # If there are, we need to use awk to replace only the domain name (the 6th field in the log)
log_write " ${CROSS} ${COL_RED}${head_line}${COL_NC} (${FAQ_BAD_ADDRESS})" # so we substitute the domain for the placeholder value
else obfuscated_line=$(echo "${line_to_obfuscate}" | awk -v placeholder="${OBFUSCATED_PLACEHOLDER}" '{sub($6,placeholder); print $0}')
# If the variable does not a value (the current default behavior), so do not obfuscate anything log_write " ${obfuscated_line}"
if [[ -z ${OBFUSCATE} ]]; then else
log_write " ${head_line}" log_write " ${line}"
# Othwerise, a flag was passed to this command to obfuscate domains in the log fi
else fi
# So first check if there are domains in the log that should be obfuscated fi
if [[ -n ${line_to_obfuscate} ]]; then done
# If there are, we need to use awk to replace only the domain name (the 6th field in the log)
# so we substitute the domain for the placeholder value
obfuscated_line=$(echo "${line_to_obfuscate}" | awk -v placeholder="${OBFUSCATED_PLACEHOLDER}" '{sub($6,placeholder); print $0}')
log_write " ${obfuscated_line}"
else
log_write " ${head_line}"
fi
fi
fi
done
log_write ""
# Set the IFS back to what it was
IFS="$OLD_IFS"
} }
tricorder_use_nc_or_curl() { analyze_pihole_log() {
# Users can submit their debug logs using nc (unencrypted) or curl (encrypted) if available echo_current_diagnostic "Pi-hole log"
# Check for curl first since encryption is a good thing local pihole_log_head=()
if command -v curl &> /dev/null; then local pihole_log_tail=()
# If the command exists, local pihole_log_permissions
log_write " * Using ${COL_GREEN}curl${COL_NC} for transmission." local logging_enabled
# transmit he log via TLS and store the token returned in a variable
tricorder_token=$(curl --silent --upload-file ${PIHOLE_DEBUG_LOG} https://tricorder.pi-hole.net:${TRICORDER_SSL_PORT_NUMBER}) logging_enabled=$(grep -c "^log-queries" /etc/dnsmasq.d/01-pihole.conf)
if [ -z "${tricorder_token}" ]; then if [[ "${logging_enabled}" == "0" ]]; then
# curl failed, fallback to nc # Inform user that logging has been disabled and pihole.log does not contain queries
log_write " * ${COL_GREEN}curl${COL_NC} failed, falling back to ${COL_YELLOW}netcat${COL_NC} for transmission." log_write "${INFO} Query logging is disabled"
tricorder_token=$(< ${PIHOLE_DEBUG_LOG} nc tricorder.pi-hole.net ${TRICORDER_NC_PORT_NUMBER}) log_write ""
fi
# Put the current Internal Field Separator into another variable so it can be restored later
OLD_IFS="$IFS"
# Get the lines that are in the file(s) and store them in an array for parsing later
IFS=$'\r\n'
pihole_log_permissions=$(ls -lhd "${PIHOLE_LOG}")
log_write "${COL_GREEN}${pihole_log_permissions}${COL_NC}"
mapfile -t pihole_log_head < <(head -n 20 ${PIHOLE_LOG})
log_write " ${COL_CYAN}-----head of $(basename ${PIHOLE_LOG})------${COL_NC}"
obfuscated_pihole_log "${pihole_log_head[@]}"
log_write ""
mapfile -t pihole_log_tail < <(tail -n 20 ${PIHOLE_LOG})
log_write " ${COL_CYAN}-----tail of $(basename ${PIHOLE_LOG})------${COL_NC}"
obfuscated_pihole_log "${pihole_log_tail[@]}"
log_write ""
# Set the IFS back to what it was
IFS="$OLD_IFS"
}
curl_to_tricorder() {
# Users can submit their debug logs using curl (encrypted)
log_write " * Using ${COL_GREEN}curl${COL_NC} for transmission."
# transmit the log via TLS and store the token returned in a variable
tricorder_token=$(curl --silent --fail --show-error --upload-file ${PIHOLE_DEBUG_LOG} https://tricorder.pi-hole.net 2>&1)
if [[ "${tricorder_token}" != "https://tricorder.pi-hole.net/"* ]]; then
log_write " * ${COL_GREEN}curl${COL_NC} failed, contact Pi-hole support for assistance."
# Log curl error (if available)
if [ -n "${tricorder_token}" ]; then
log_write " * Error message: ${COL_RED}${tricorder_token}${COL_NC}\\n"
tricorder_token=""
fi fi
# Otherwise,
else
# use net cat
log_write "${INFO} Using ${COL_YELLOW}netcat${COL_NC} for transmission."
# Save the token returned by our server in a variable
tricorder_token=$(< ${PIHOLE_DEBUG_LOG} nc tricorder.pi-hole.net ${TRICORDER_NC_PORT_NUMBER})
fi fi
} }
@ -1370,14 +1385,13 @@ upload_to_tricorder() {
# Provide information on what they should do with their token # Provide information on what they should do with their token
log_write " * The debug log can be uploaded to tricorder.pi-hole.net for sharing with developers only." log_write " * The debug log can be uploaded to tricorder.pi-hole.net for sharing with developers only."
log_write " * For more information, see: ${TRICORDER_CONTEST}"
log_write " * If available, we'll use openssl to upload the log, otherwise it will fall back to netcat."
# If pihole -d is running automatically (usually through the dashboard) # If pihole -d is running automatically (usually through the dashboard)
if [[ "${AUTOMATED}" ]]; then if [[ "${AUTOMATED}" ]]; then
# let the user know # let the user know
log_write "${INFO} Debug script running in automated mode" log_write "${INFO} Debug script running in automated mode"
# and then decide again which tool to use to submit it # and then decide again which tool to use to submit it
tricorder_use_nc_or_curl curl_to_tricorder
# If we're not running in automated mode, # If we're not running in automated mode,
else else
echo "" echo ""
@ -1386,7 +1400,7 @@ upload_to_tricorder() {
read -r -p "[?] Would you like to upload the log? [y/N] " response read -r -p "[?] Would you like to upload the log? [y/N] " response
case ${response} in case ${response} in
# If they say yes, run our function for uploading the log # If they say yes, run our function for uploading the log
[yY][eE][sS]|[yY]) tricorder_use_nc_or_curl;; [yY][eE][sS]|[yY]) curl_to_tricorder;;
# If they choose no, just exit out of the script # If they choose no, just exit out of the script
*) log_write " * Log will ${COL_GREEN}NOT${COL_NC} be uploaded to tricorder.\\n * A local copy of the debug log can be found at: ${COL_CYAN}${PIHOLE_DEBUG_LOG}${COL_NC}\\n";exit; *) log_write " * Log will ${COL_GREEN}NOT${COL_NC} be uploaded to tricorder.\\n * A local copy of the debug log can be found at: ${COL_CYAN}${PIHOLE_DEBUG_LOG}${COL_NC}\\n";exit;
esac esac
@ -1397,15 +1411,15 @@ upload_to_tricorder() {
# Again, try to make this visually striking so the user realizes they need to do something with this information # Again, try to make this visually striking so the user realizes they need to do something with this information
# Namely, provide the Pi-hole devs with the token # Namely, provide the Pi-hole devs with the token
log_write "" log_write ""
log_write "${COL_PURPLE}***********************************${COL_NC}" log_write "${COL_PURPLE}*****************************************************************${COL_NC}"
log_write "${COL_PURPLE}***********************************${COL_NC}" log_write "${COL_PURPLE}*****************************************************************${COL_NC}\\n"
log_write "${TICK} Your debug token is: ${COL_GREEN}${tricorder_token}${COL_NC}" log_write "${TICK} Your debug token is: ${COL_GREEN}${tricorder_token}${COL_NC}"
log_write "${COL_PURPLE}***********************************${COL_NC}" log_write "${INFO}${COL_RED} Logs are deleted 48 hours after upload.${COL_NC}\\n"
log_write "${COL_PURPLE}***********************************${COL_NC}" log_write "${COL_PURPLE}*****************************************************************${COL_NC}"
log_write "${COL_PURPLE}*****************************************************************${COL_NC}"
log_write "" log_write ""
log_write " * Provide the token above to the Pi-hole team for assistance at" log_write " * Provide the token above to the Pi-hole team for assistance at ${FORUMS_URL}"
log_write " * ${FORUMS_URL}"
log_write " * Your log will self-destruct on our server after ${COL_RED}48 hours${COL_NC}."
# If no token was generated # If no token was generated
else else
# Show an error and some help instructions # Show an error and some help instructions
@ -1428,6 +1442,7 @@ diagnose_operating_system
check_selinux check_selinux
check_firewalld check_firewalld
processor_check processor_check
disk_usage
check_networking check_networking
check_name_resolution check_name_resolution
check_dhcp_servers check_dhcp_servers

View file

@ -11,6 +11,11 @@
colfile="/opt/pihole/COL_TABLE" colfile="/opt/pihole/COL_TABLE"
source ${colfile} source ${colfile}
# In case we're running at the same time as a system logrotate, use a
# separate logrotate state file to prevent stepping on each other's
# toes.
STATEFILE="/var/lib/logrotate/pihole"
# Determine database location # Determine database location
# Obtain DBFILE=... setting from pihole-FTL.db # Obtain DBFILE=... setting from pihole-FTL.db
# Constructed to return nothing when # Constructed to return nothing when
@ -32,7 +37,7 @@ if [[ "$@" == *"once"* ]]; then
# Nightly logrotation # Nightly logrotation
if command -v /usr/sbin/logrotate >/dev/null; then if command -v /usr/sbin/logrotate >/dev/null; then
# Logrotate once # Logrotate once
/usr/sbin/logrotate --force /etc/pihole/logrotate /usr/sbin/logrotate --force --state "${STATEFILE}" /etc/pihole/logrotate
else else
# Copy pihole.log over to pihole.log.1 # Copy pihole.log over to pihole.log.1
# and empty out pihole.log # and empty out pihole.log
@ -47,8 +52,8 @@ else
# Manual flushing # Manual flushing
if command -v /usr/sbin/logrotate >/dev/null; then if command -v /usr/sbin/logrotate >/dev/null; then
# Logrotate twice to move all data out of sight of FTL # Logrotate twice to move all data out of sight of FTL
/usr/sbin/logrotate --force /etc/pihole/logrotate; sleep 3 /usr/sbin/logrotate --force --state "${STATEFILE}" /etc/pihole/logrotate; sleep 3
/usr/sbin/logrotate --force /etc/pihole/logrotate /usr/sbin/logrotate --force --state "${STATEFILE}" /etc/pihole/logrotate
else else
# Flush both pihole.log and pihole.log.1 (if existing) # Flush both pihole.log and pihole.log.1 (if existing)
echo " " > /var/log/pihole.log echo " " > /var/log/pihole.log

View file

@ -1,5 +1,6 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# shellcheck disable=SC1090 # shellcheck disable=SC1090
# Pi-hole: A black hole for Internet advertisements # Pi-hole: A black hole for Internet advertisements
# (c) 2018 Pi-hole, LLC (https://pi-hole.net) # (c) 2018 Pi-hole, LLC (https://pi-hole.net)
# Network-wide ad blocking via your own hardware. # Network-wide ad blocking via your own hardware.
@ -11,12 +12,21 @@
# Globals # Globals
piholeDir="/etc/pihole" piholeDir="/etc/pihole"
gravityDBfile="${piholeDir}/gravity.db" GRAVITYDB="${piholeDir}/gravity.db"
options="$*" options="$*"
all="" all=""
exact="" exact=""
blockpage="" blockpage=""
matchType="match" matchType="match"
# Source pihole-FTL from install script
pihole_FTL="${piholeDir}/pihole-FTL.conf"
if [[ -f "${pihole_FTL}" ]]; then
source "${pihole_FTL}"
fi
# Set this only after sourcing pihole-FTL.conf as the gravity database path may
# have changed
gravityDBfile="${GRAVITYDB}"
colfile="/opt/pihole/COL_TABLE" colfile="/opt/pihole/COL_TABLE"
source "${colfile}" source "${colfile}"

View file

@ -95,6 +95,10 @@ main() {
# shellcheck disable=1090,2154 # shellcheck disable=1090,2154
source "${setupVars}" source "${setupVars}"
# Install packages used by this installation script (necessary if users have removed e.g. git from their systems)
package_manager_detect
install_dependent_packages "${INSTALLER_DEPS[@]}"
# This is unlikely # This is unlikely
if ! is_repo "${PI_HOLE_FILES_DIR}" ; then if ! is_repo "${PI_HOLE_FILES_DIR}" ; then
echo -e "\\n ${COL_LIGHT_RED}Error: Core Pi-hole repo is missing from system!" echo -e "\\n ${COL_LIGHT_RED}Error: Core Pi-hole repo is missing from system!"

View file

@ -153,7 +153,7 @@ versionOutput() {
if [[ -n "$current" ]] && [[ -n "$latest" ]]; then if [[ -n "$current" ]] && [[ -n "$latest" ]]; then
output="${1^} version is $branch$current (Latest: $latest)" output="${1^} version is $branch$current (Latest: $latest)"
elif [[ -n "$current" ]] && [[ -z "$latest" ]]; then elif [[ -n "$current" ]] && [[ -z "$latest" ]]; then
output="Current ${1^} version is $branch$current." output="Current ${1^} version is $branch$current"
elif [[ -z "$current" ]] && [[ -n "$latest" ]]; then elif [[ -z "$current" ]] && [[ -n "$latest" ]]; then
output="Latest ${1^} version is $latest" output="Latest ${1^} version is $latest"
elif [[ "$curHash" == "N/A" ]] || [[ "$latHash" == "N/A" ]]; then elif [[ "$curHash" == "N/A" ]] || [[ "$latHash" == "N/A" ]]; then

View file

@ -44,7 +44,8 @@ Options:
-e, email Set an administrative contact address for the Block Page -e, email Set an administrative contact address for the Block Page
-h, --help Show this help dialog -h, --help Show this help dialog
-i, interface Specify dnsmasq's interface listening behavior -i, interface Specify dnsmasq's interface listening behavior
-l, privacylevel Set privacy level (0 = lowest, 3 = highest)" -l, privacylevel Set privacy level (0 = lowest, 3 = highest)
-t, teleporter Backup configuration as an archive"
exit 0 exit 0
} }
@ -53,7 +54,7 @@ add_setting() {
} }
delete_setting() { delete_setting() {
sed -i "/${1}/d" "${setupVars}" sed -i "/^${1}/d" "${setupVars}"
} }
change_setting() { change_setting() {
@ -66,7 +67,7 @@ addFTLsetting() {
} }
deleteFTLsetting() { deleteFTLsetting() {
sed -i "/${1}/d" "${FTLconf}" sed -i "/^${1}/d" "${FTLconf}"
} }
changeFTLsetting() { changeFTLsetting() {
@ -83,7 +84,7 @@ add_dnsmasq_setting() {
} }
delete_dnsmasq_setting() { delete_dnsmasq_setting() {
sed -i "/${1}/d" "${dnsmasqconfig}" sed -i "/^${1}/d" "${dnsmasqconfig}"
} }
SetTemperatureUnit() { SetTemperatureUnit() {
@ -238,18 +239,18 @@ trust-anchor=.,20326,8,2,E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC68345710423
# 168.192.in-addr.arpa to 192.168.0.0/16 # 168.192.in-addr.arpa to 192.168.0.0/16
# 192.in-addr.arpa to 192.0.0.0/8 # 192.in-addr.arpa to 192.0.0.0/8
if [[ "${CONDITIONAL_FORWARDING_REVERSE}" == *"in-addr.arpa" ]];then if [[ "${CONDITIONAL_FORWARDING_REVERSE}" == *"in-addr.arpa" ]];then
arrRev=("${CONDITIONAL_FORWARDING_REVERSE//./ }") arrRev=("${CONDITIONAL_FORWARDING_REVERSE//./ }")
case ${#arrRev[@]} in case ${#arrRev[@]} in
6 ) REV_SERVER_CIDR="${arrRev[3]}.${arrRev[2]}.${arrRev[1]}.${arrRev[0]}/32";; 6 ) REV_SERVER_CIDR="${arrRev[3]}.${arrRev[2]}.${arrRev[1]}.${arrRev[0]}/32";;
5 ) REV_SERVER_CIDR="${arrRev[2]}.${arrRev[1]}.${arrRev[0]}.0/24";; 5 ) REV_SERVER_CIDR="${arrRev[2]}.${arrRev[1]}.${arrRev[0]}.0/24";;
4 ) REV_SERVER_CIDR="${arrRev[1]}.${arrRev[0]}.0.0/16";; 4 ) REV_SERVER_CIDR="${arrRev[1]}.${arrRev[0]}.0.0/16";;
3 ) REV_SERVER_CIDR="${arrRev[0]}.0.0.0/8";; 3 ) REV_SERVER_CIDR="${arrRev[0]}.0.0.0/8";;
esac esac
else else
# Set REV_SERVER_CIDR to whatever value it was set to # Set REV_SERVER_CIDR to whatever value it was set to
REV_SERVER_CIDR="${CONDITIONAL_FORWARDING_REVERSE}" REV_SERVER_CIDR="${CONDITIONAL_FORWARDING_REVERSE}"
fi fi
# If REV_SERVER_CIDR is not converted by the above, then use the REV_SERVER_TARGET variable to derive it # If REV_SERVER_CIDR is not converted by the above, then use the REV_SERVER_TARGET variable to derive it
if [ -z "${REV_SERVER_CIDR}" ]; then if [ -z "${REV_SERVER_CIDR}" ]; then
# Convert existing input to /24 subnet (preserves legacy behavior) # Convert existing input to /24 subnet (preserves legacy behavior)
@ -266,17 +267,22 @@ trust-anchor=.,20326,8,2,E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC68345710423
delete_setting "CONDITIONAL_FORWARDING_IP" delete_setting "CONDITIONAL_FORWARDING_IP"
fi fi
delete_dnsmasq_setting "rev-server"
if [[ "${REV_SERVER}" == true ]]; then if [[ "${REV_SERVER}" == true ]]; then
add_dnsmasq_setting "rev-server=${REV_SERVER_CIDR},${REV_SERVER_TARGET}" add_dnsmasq_setting "rev-server=${REV_SERVER_CIDR},${REV_SERVER_TARGET}"
if [ -n "${REV_SERVER_DOMAIN}" ]; then if [ -n "${REV_SERVER_DOMAIN}" ]; then
# Forward local domain names to the CF target, too
add_dnsmasq_setting "server=/${REV_SERVER_DOMAIN}/${REV_SERVER_TARGET}" add_dnsmasq_setting "server=/${REV_SERVER_DOMAIN}/${REV_SERVER_TARGET}"
fi fi
fi
# Prevent Firefox from automatically switching over to DNS-over-HTTPS if [[ "${DNS_FQDN_REQUIRED}" != true ]]; then
# This follows https://support.mozilla.org/en-US/kb/configuring-networks-disable-dns-over-https # Forward unqualified names to the CF target only when the "never
# (sourced 7th September 2019) # forward non-FQDN" option is unticked
add_dnsmasq_setting "server=/use-application-dns.net/" add_dnsmasq_setting "server=//${REV_SERVER_TARGET}"
fi
fi
# We need to process DHCP settings here as well to account for possible # We need to process DHCP settings here as well to account for possible
# changes in the non-FQDN forwarding. This cannot be done in 01-pihole.conf # changes in the non-FQDN forwarding. This cannot be done in 01-pihole.conf
@ -426,7 +432,7 @@ dhcp-leasefile=/etc/pihole/dhcp.leases
echo "#quiet-dhcp6 echo "#quiet-dhcp6
#enable-ra #enable-ra
dhcp-option=option6:dns-server,[::] dhcp-option=option6:dns-server,[::]
dhcp-range=::100,::1ff,constructor:${interface},ra-names,slaac,${leasetime} dhcp-range=::100,::1ff,constructor:${interface},ra-names,slaac,64,3600
ra-param=*,0,0 ra-param=*,0,0
" >> "${dhcpconfig}" " >> "${dhcpconfig}"
fi fi
@ -564,7 +570,13 @@ AddDHCPStaticAddress() {
RemoveDHCPStaticAddress() { RemoveDHCPStaticAddress() {
mac="${args[2]}" mac="${args[2]}"
sed -i "/dhcp-host=${mac}.*/d" "${dhcpstaticconfig}" if [[ "$mac" =~ ^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$ ]]; then
sed -i "/dhcp-host=${mac}.*/d" "${dhcpstaticconfig}"
else
echo " ${CROSS} Invalid Mac Passed!"
exit 1
fi
} }
SetAdminEmail() { SetAdminEmail() {
@ -636,8 +648,11 @@ Interfaces:
Teleporter() { Teleporter() {
local datetimestamp local datetimestamp
local host
datetimestamp=$(date "+%Y-%m-%d_%H-%M-%S") datetimestamp=$(date "+%Y-%m-%d_%H-%M-%S")
php /var/www/html/admin/scripts/pi-hole/php/teleporter.php > "pi-hole-teleporter_${datetimestamp}.tar.gz" host=$(hostname)
host="${host//./_}"
php /var/www/html/admin/scripts/pi-hole/php/teleporter.php > "pi-hole-${host:-noname}-teleporter_${datetimestamp}.tar.gz"
} }
checkDomain() checkDomain()
@ -694,10 +709,25 @@ AddCustomDNSAddress() {
ip="${args[2]}" ip="${args[2]}"
host="${args[3]}" host="${args[3]}"
echo "${ip} ${host}" >> "${dnscustomfile}" reload="${args[4]}"
# Restart dnsmasq to load new custom DNS entries validHost="$(checkDomain "${host}")"
RestartDNS if [[ -n "${validHost}" ]]; then
if valid_ip "${ip}" || valid_ip6 "${ip}" ; then
echo "${ip} ${validHost}" >> "${dnscustomfile}"
else
echo -e " ${CROSS} Invalid IP has been passed"
exit 1
fi
else
echo " ${CROSS} Invalid Domain passed!"
exit 1
fi
# Restart dnsmasq to load new custom DNS entries only if $reload not false
if [[ ! $reload == "false" ]]; then
RestartDNS
fi
} }
RemoveCustomDNSAddress() { RemoveCustomDNSAddress() {
@ -705,10 +735,25 @@ RemoveCustomDNSAddress() {
ip="${args[2]}" ip="${args[2]}"
host="${args[3]}" host="${args[3]}"
sed -i "/${ip} ${host}/d" "${dnscustomfile}" reload="${args[4]}"
# Restart dnsmasq to update removed custom DNS entries validHost="$(checkDomain "${host}")"
RestartDNS if [[ -n "${validHost}" ]]; then
if valid_ip "${ip}" || valid_ip6 "${ip}" ; then
sed -i "/^${ip} ${validHost}$/d" "${dnscustomfile}"
else
echo -e " ${CROSS} Invalid IP has been passed"
exit 1
fi
else
echo " ${CROSS} Invalid Domain passed!"
exit 1
fi
# Restart dnsmasq to load new custom DNS entries only if reload is not false
if [[ ! $reload == "false" ]]; then
RestartDNS
fi
} }
AddCustomCNAMERecord() { AddCustomCNAMERecord() {
@ -716,10 +761,25 @@ AddCustomCNAMERecord() {
domain="${args[2]}" domain="${args[2]}"
target="${args[3]}" target="${args[3]}"
echo "cname=${domain},${target}" >> "${dnscustomcnamefile}" reload="${args[4]}"
# Restart dnsmasq to load new custom CNAME records validDomain="$(checkDomain "${domain}")"
RestartDNS if [[ -n "${validDomain}" ]]; then
validTarget="$(checkDomain "${target}")"
if [[ -n "${validTarget}" ]]; then
echo "cname=${validDomain},${validTarget}" >> "${dnscustomcnamefile}"
else
echo " ${CROSS} Invalid Target Passed!"
exit 1
fi
else
echo " ${CROSS} Invalid Domain passed!"
exit 1
fi
# Restart dnsmasq to load new custom CNAME records only if reload is not false
if [[ ! $reload == "false" ]]; then
RestartDNS
fi
} }
RemoveCustomCNAMERecord() { RemoveCustomCNAMERecord() {
@ -727,10 +787,26 @@ RemoveCustomCNAMERecord() {
domain="${args[2]}" domain="${args[2]}"
target="${args[3]}" target="${args[3]}"
sed -i "/cname=${domain},${target}/d" "${dnscustomcnamefile}" reload="${args[4]}"
# Restart dnsmasq to update removed custom CNAME records validDomain="$(checkDomain "${domain}")"
RestartDNS if [[ -n "${validDomain}" ]]; then
validTarget="$(checkDomain "${target}")"
if [[ -n "${validTarget}" ]]; then
sed -i "/cname=${validDomain},${validTarget}$/d" "${dnscustomcnamefile}"
else
echo " ${CROSS} Invalid Target Passed!"
exit 1
fi
else
echo " ${CROSS} Invalid Domain passed!"
exit 1
fi
# Restart dnsmasq to update removed custom CNAME records only if $reload not false
if [[ ! $reload == "false" ]]; then
RestartDNS
fi
} }
main() { main() {

View file

@ -1,28 +0,0 @@
#!/usr/bin/env bash
# Pi-hole: A black hole for Internet advertisements
# (c) 2017 Pi-hole, LLC (https://pi-hole.net)
# Network-wide ad blocking via your own hardware.
#
# Provides an automated migration subroutine to convert Pi-hole v3.x wildcard domains to Pi-hole v4.x regex filters
#
# This file is copyright under the latest version of the EUPL.
# Please see LICENSE file for your rights under this license.
# regexFile set in gravity.sh
wildcardFile="/etc/dnsmasq.d/03-pihole-wildcard.conf"
convert_wildcard_to_regex() {
if [ ! -f "${wildcardFile}" ]; then
return
fi
local addrlines domains uniquedomains
# Obtain wildcard domains from old file
addrlines="$(grep -oE "/.*/" ${wildcardFile})"
# Strip "/" from domain names and convert "." to regex-compatible "\."
domains="$(sed 's/\///g;s/\./\\./g' <<< "${addrlines}")"
# Remove repeated domains (may have been inserted two times due to A and AAAA blocking)
uniquedomains="$(uniq <<< "${domains}")"
# Automatically generate regex filters and remove old wildcards file
awk '{print "(^|\\.)"$0"$"}' <<< "${uniquedomains}" >> "${regexFile:?}" && rm "${wildcardFile}"
}

View file

@ -32,7 +32,10 @@ CREATE TABLE adlist
date_added INTEGER NOT NULL DEFAULT (cast(strftime('%s', 'now') as int)), date_added INTEGER NOT NULL DEFAULT (cast(strftime('%s', 'now') as int)),
date_modified INTEGER NOT NULL DEFAULT (cast(strftime('%s', 'now') as int)), date_modified INTEGER NOT NULL DEFAULT (cast(strftime('%s', 'now') as int)),
comment TEXT, comment TEXT,
date_updated INTEGER date_updated INTEGER,
number INTEGER NOT NULL DEFAULT 0,
invalid_domains INTEGER NOT NULL DEFAULT 0,
status INTEGER NOT NULL DEFAULT 0
); );
CREATE TABLE adlist_by_group CREATE TABLE adlist_by_group
@ -54,7 +57,7 @@ CREATE TABLE info
value TEXT NOT NULL value TEXT NOT NULL
); );
INSERT INTO "info" VALUES('version','13'); INSERT INTO "info" VALUES('version','15');
CREATE TABLE domain_audit CREATE TABLE domain_audit
( (
@ -140,12 +143,10 @@ CREATE VIEW vw_gravity AS SELECT domain, adlist_by_group.group_id AS group_id
LEFT JOIN "group" ON "group".id = adlist_by_group.group_id LEFT JOIN "group" ON "group".id = adlist_by_group.group_id
WHERE adlist.enabled = 1 AND (adlist_by_group.group_id IS NULL OR "group".enabled = 1); WHERE adlist.enabled = 1 AND (adlist_by_group.group_id IS NULL OR "group".enabled = 1);
CREATE VIEW vw_adlist AS SELECT DISTINCT address, adlist.id AS id CREATE VIEW vw_adlist AS SELECT DISTINCT address, id
FROM adlist FROM adlist
LEFT JOIN adlist_by_group ON adlist_by_group.adlist_id = adlist.id WHERE enabled = 1
LEFT JOIN "group" ON "group".id = adlist_by_group.group_id ORDER BY id;
WHERE adlist.enabled = 1 AND (adlist_by_group.group_id IS NULL OR "group".enabled = 1)
ORDER BY adlist.id;
CREATE TRIGGER tr_domainlist_add AFTER INSERT ON domainlist CREATE TRIGGER tr_domainlist_add AFTER INSERT ON domainlist
BEGIN BEGIN

View file

@ -1,4 +1,4 @@
#!/usr/bin/env bash #!/usr/bin/env sh
### BEGIN INIT INFO ### BEGIN INIT INFO
# Provides: pihole-FTL # Provides: pihole-FTL
# Required-Start: $remote_fs $syslog $network # Required-Start: $remote_fs $syslog $network
@ -9,11 +9,8 @@
# Description: Enable service provided by pihole-FTL daemon # Description: Enable service provided by pihole-FTL daemon
### END INIT INFO ### END INIT INFO
FTLUSER=pihole
PIDFILE=/run/pihole-FTL.pid
is_running() { is_running() {
pgrep -o "pihole-FTL" > /dev/null 2>&1 pgrep -xo "pihole-FTL" > /dev/null
} }
@ -23,27 +20,22 @@ start() {
echo "pihole-FTL is already running" echo "pihole-FTL is already running"
else else
# Touch files to ensure they exist (create if non-existing, preserve if existing) # Touch files to ensure they exist (create if non-existing, preserve if existing)
touch /var/log/pihole-FTL.log /var/log/pihole.log mkdir -pm 0755 /run/pihole
touch /run/pihole-FTL.pid /run/pihole-FTL.port touch /run/pihole-FTL.pid /run/pihole-FTL.port /var/log/pihole-FTL.log /var/log/pihole.log /etc/pihole/dhcp.leases
touch /etc/pihole/dhcp.leases
mkdir -p /run/pihole
mkdir -p /var/log/pihole
chown pihole:pihole /run/pihole /var/log/pihole
# Remove possible leftovers from previous pihole-FTL processes
rm -f /dev/shm/FTL-* 2> /dev/null
rm /run/pihole/FTL.sock 2> /dev/null
# Ensure that permissions are set so that pihole-FTL can edit all necessary files # Ensure that permissions are set so that pihole-FTL can edit all necessary files
chown pihole:pihole /run/pihole-FTL.pid /run/pihole-FTL.port chown pihole:pihole /run/pihole-FTL.pid /run/pihole-FTL.port /var/log/pihole-FTL.log /var/log/pihole.log /etc/pihole/dhcp.leases /run/pihole /etc/pihole
chown pihole:pihole /etc/pihole /etc/pihole/dhcp.leases 2> /dev/null chmod 0644 /run/pihole-FTL.pid /run/pihole-FTL.port /var/log/pihole-FTL.log /var/log/pihole.log /etc/pihole/dhcp.leases
chown pihole:pihole /var/log/pihole-FTL.log /var/log/pihole.log # Ensure that permissions are set so that pihole-FTL can edit the files. We ignore errors as the file may not (yet) exist
chmod 0644 /var/log/pihole-FTL.log /run/pihole-FTL.pid /run/pihole-FTL.port /var/log/pihole.log chmod -f 0644 /etc/pihole/macvendor.db
# Chown database files to the user FTL runs as. We ignore errors as the files may not (yet) exist # Chown database files to the user FTL runs as. We ignore errors as the files may not (yet) exist
chown pihole:pihole /etc/pihole/pihole-FTL.db /etc/pihole/gravity.db 2> /dev/null chown -f pihole:pihole /etc/pihole/pihole-FTL.db /etc/pihole/gravity.db /etc/pihole/macvendor.db
if setcap CAP_NET_BIND_SERVICE,CAP_NET_RAW,CAP_NET_ADMIN,CAP_SYS_NICE+eip "$(which pihole-FTL)"; then # Chown database file permissions so that the pihole group (web interface) can edit the file. We ignore errors as the files may not (yet) exist
su -s /bin/sh -c "/usr/bin/pihole-FTL" "$FTLUSER" chmod -f 0664 /etc/pihole/pihole-FTL.db
if setcap CAP_NET_BIND_SERVICE,CAP_NET_RAW,CAP_NET_ADMIN,CAP_SYS_NICE,CAP_IPC_LOCK,CAP_CHOWN+eip "/usr/bin/pihole-FTL"; then
su -s /bin/sh -c "/usr/bin/pihole-FTL" pihole
else else
echo "Warning: Starting pihole-FTL as root because setting capabilities is not supported on this system" echo "Warning: Starting pihole-FTL as root because setting capabilities is not supported on this system"
pihole-FTL /usr/bin/pihole-FTL
fi fi
echo echo
fi fi
@ -52,20 +44,20 @@ start() {
# Stop the service # Stop the service
stop() { stop() {
if is_running; then if is_running; then
pkill -o pihole-FTL pkill -xo "pihole-FTL"
for i in {1..5}; do for i in 1 2 3 4 5; do
if ! is_running; then if ! is_running; then
break break
fi fi
echo -n "." printf "."
sleep 1 sleep 1
done done
echo echo
if is_running; then if is_running; then
echo "Not stopped; may still be shutting down or shutdown may have failed, killing now" echo "Not stopped; may still be shutting down or shutdown may have failed, killing now"
pkill -o -9 pihole-FTL pkill -xo -9 "pihole-FTL"
exit 1 exit 1
else else
echo "Stopped" echo "Stopped"
@ -73,6 +65,8 @@ stop() {
else else
echo "Not running" echo "Not running"
fi fi
# Cleanup
rm -f /run/pihole/FTL.sock /dev/shm/FTL-*
echo echo
} }
@ -101,7 +95,7 @@ case "$1" in
start start
;; ;;
*) *)
echo $"Usage: $0 {start|stop|restart|reload|status}" echo "Usage: $0 {start|stop|restart|reload|status}"
exit 1 exit 1
esac esac

View file

@ -26,7 +26,7 @@
# parameter "quiet": don't print messages # parameter "quiet": don't print messages
00 00 * * * root PATH="$PATH:/usr/sbin:/usr/local/bin/" pihole flush once quiet 00 00 * * * root PATH="$PATH:/usr/sbin:/usr/local/bin/" pihole flush once quiet
@reboot root /usr/sbin/logrotate /etc/pihole/logrotate @reboot root /usr/sbin/logrotate --state /var/lib/logrotate/pihole /etc/pihole/logrotate
# Pi-hole: Grab local version and branch every 10 minutes # Pi-hole: Grab local version and branch every 10 minutes
*/10 * * * * root PATH="$PATH:/usr/sbin:/usr/local/bin/" pihole updatechecker local */10 * * * * root PATH="$PATH:/usr/sbin:/usr/local/bin/" pihole updatechecker local

View file

@ -145,7 +145,17 @@ body {
} }
/* User is greeted with a splash page when browsing to Pi-hole IP address */ /* User is greeted with a splash page when browsing to Pi-hole IP address */
#splashpage { background: #222; color: rgba(255, 255, 255, 0.7); text-align: center; } #splashpage {
background: #222;
color: rgba(255, 255, 255, 0.7);
text-align: center;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
#splashpage img { margin: 5px; width: 256px; } #splashpage img { margin: 5px; width: 256px; }
#splashpage b { color: inherit; } #splashpage b { color: inherit; }
@ -196,6 +206,26 @@ header #bpAlt label {
display: block; display: block;
} }
html, body {
height: 100%;
}
#pihole_card {
width: 400px;
height: auto;
max-width: 400px;
}
#pihole_card p, #pihole_card a {
font-size: 13pt;
text-align: center;
}
#pihole_logo_splash {
height: auto;
width: 100%;
}
/* Click anywhere else on screen to hide #bpAbout */ /* Click anywhere else on screen to hide #bpAbout */
#bpAboutToggle:checked { #bpAboutToggle:checked {
display: block; display: block;
@ -382,12 +412,44 @@ footer {
/* Responsive Content */ /* Responsive Content */
@media only screen and (max-width: 500px) { @media only screen and (max-width: 500px) {
h1 a { font-size: 1.8rem; min-width: 170px; } h1 a {
footer span::before { content: "Generated "; } font-size: 1.8rem;
footer span { display: block; } min-width: 170px;
}
footer span::before {
content: "Generated ";
}
footer span {
display: block;
}
} }
@media only screen and (min-width: 1251px) { @media only screen and (min-width: 1251px) {
#bpWrapper, footer { border-radius: 0 0 5px 5px; } #bpWrapper, footer {
#bpAbout { border-right-width: 1px; } border-radius: 0 0 5px 5px;
}
#bpAbout {
border-right-width: 1px;
}
}
@media only screen and (max-width: 400px) {
#pihole_card {
width: 100%;
height: auto;
}
#pihole_card p, #pihole_card a {
font-size: 100%;
}
}
@media only screen and (max-width: 256px) {
#pihole_logo_splash {
width: 90% !important;
height: auto;
}
} }

View file

@ -58,31 +58,33 @@ if ($serverName === "pi.hole"
// When directly browsing via IP or authorized hostname // When directly browsing via IP or authorized hostname
// Render splash/landing page based off presence of $landPage file // Render splash/landing page based off presence of $landPage file
// Unset variables so as to not be included in $landPage or $splashPage // Unset variables so as to not be included in $landPage or $splashPage
unset($serverName, $svPasswd, $svEmail, $authorizedHosts, $validExtTypes, $currentUrlExt, $viewPort); unset($svPasswd, $svEmail, $authorizedHosts, $validExtTypes, $currentUrlExt);
// If $landPage file is present // If $landPage file is present
if (is_file(getcwd()."/$landPage")) { if (is_file(getcwd()."/$landPage")) {
unset($serverName, $viewPort); // unset extra variables not to be included in $landpage
include $landPage; include $landPage;
exit(); exit();
} }
// If $landPage file was not present, Set Splash Page output // If $landPage file was not present, Set Splash Page output
$splashPage = " $splashPage = <<<EOT
<!doctype html> <!doctype html>
<html lang='en'> <html lang='en'>
<head> <head>
<meta charset='utf-8'> <meta charset='utf-8'>
$viewPort $viewPort
<title> $serverName</title> <title> $serverName</title>
<link rel='stylesheet' href='pihole/blockingpage.css'> <link rel='stylesheet' href='/pihole/blockingpage.css'>
<link rel='shortcut icon' href='admin/img/favicons/favicon.ico' type='image/x-icon'> <link rel='shortcut icon' href='/admin/img/favicons/favicon.ico' type='image/x-icon'>
</head> </head>
<body id='splashpage'> <body id='splashpage'>
<img src='admin/img/logo.svg' alt='Pi-hole logo' width='256' height='377'> <div id="pihole_card">
<br> <img src='/admin/img/logo.svg' alt='Pi-hole logo' id="pihole_logo_splash" />
<p>Pi-<strong>hole</strong>: Your black hole for Internet advertisements</p> <p>Pi-<strong>hole</strong>: Your black hole for Internet advertisements</p>
<a href='/admin'>Did you mean to go to the admin panel?</a> <a href='/admin'>Did you mean to go to the admin panel?</a>
</div>
</body> </body>
</html> </html>
"; EOT;
exit($splashPage); exit($splashPage);
} elseif ($currentUrlExt === "js") { } elseif ($currentUrlExt === "js") {
// Serve Pi-hole JavaScript for blocked domains requesting JS // Serve Pi-hole JavaScript for blocked domains requesting JS

View file

@ -20,7 +20,6 @@ server.modules = (
"mod_accesslog", "mod_accesslog",
"mod_auth", "mod_auth",
"mod_expire", "mod_expire",
"mod_compress",
"mod_redirect", "mod_redirect",
"mod_setenv", "mod_setenv",
"mod_rewrite" "mod_rewrite"
@ -41,26 +40,6 @@ index-file.names = ( "index.php", "index.html", "index.lighttpd.html"
url.access-deny = ( "~", ".inc", ".md", ".yml", ".ini" ) url.access-deny = ( "~", ".inc", ".md", ".yml", ".ini" )
static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" ) static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" )
compress.cache-dir = "/var/cache/lighttpd/compress/"
compress.filetype = (
"application/json",
"application/vnd.ms-fontobject",
"application/xml",
"font/eot",
"font/opentype",
"font/otf",
"font/ttf",
"image/bmp",
"image/svg+xml",
"image/vnd.microsoft.icon",
"image/x-icon",
"text/css",
"text/html",
"text/javascript",
"text/plain",
"text/xml"
)
mimetype.assign = ( mimetype.assign = (
".ico" => "image/x-icon", ".ico" => "image/x-icon",
".jpeg" => "image/jpeg", ".jpeg" => "image/jpeg",
@ -81,6 +60,10 @@ mimetype.assign = (
".woff2" => "font/woff2" ".woff2" => "font/woff2"
) )
# Add user chosen options held in external file
# This uses include_shell instead of an include wildcard for compatibility
include_shell "cat external.conf 2>/dev/null"
# default listening port for IPv6 falls back to the IPv4 port # default listening port for IPv6 falls back to the IPv4 port
include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port
@ -95,11 +78,6 @@ $HTTP["url"] =~ "^/admin/" {
"X-Pi-hole" => "The Pi-hole Web interface is working!", "X-Pi-hole" => "The Pi-hole Web interface is working!",
"X-Frame-Options" => "DENY" "X-Frame-Options" => "DENY"
) )
$HTTP["url"] =~ "\.(eot|otf|tt[cf]|woff2?)$" {
# Allow Block Page access to local fonts
setenv.add-response-header = ( "Access-Control-Allow-Origin" => "*" )
}
} }
# Block . files from being served, such as .git, .github, .gitignore # Block . files from being served, such as .git, .github, .gitignore
@ -107,9 +85,12 @@ $HTTP["url"] =~ "^/admin/\.(.*)" {
url.access-deny = ("") url.access-deny = ("")
} }
# allow teleporter iframe on settings page
$HTTP["url"] =~ "/teleporter\.php$" {
$HTTP["referer"] =~ "/admin/settings\.php" {
setenv.add-response-header = ( "X-Frame-Options" => "SAMEORIGIN" )
}
}
# Default expire header # Default expire header
expire.url = ( "" => "access plus 0 seconds" ) expire.url = ( "" => "access plus 0 seconds" )
# Add user chosen options held in external file
# This uses include_shell instead of an include wildcard for compatibility
include_shell "cat external.conf 2>/dev/null"

View file

@ -21,7 +21,6 @@ server.modules = (
"mod_expire", "mod_expire",
"mod_fastcgi", "mod_fastcgi",
"mod_accesslog", "mod_accesslog",
"mod_compress",
"mod_redirect", "mod_redirect",
"mod_setenv", "mod_setenv",
"mod_rewrite" "mod_rewrite"
@ -42,26 +41,6 @@ index-file.names = ( "index.php", "index.html", "index.lighttpd.html"
url.access-deny = ( "~", ".inc", ".md", ".yml", ".ini" ) url.access-deny = ( "~", ".inc", ".md", ".yml", ".ini" )
static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" ) static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" )
compress.cache-dir = "/var/cache/lighttpd/compress/"
compress.filetype = (
"application/json",
"application/vnd.ms-fontobject",
"application/xml",
"font/eot",
"font/opentype",
"font/otf",
"font/ttf",
"image/bmp",
"image/svg+xml",
"image/vnd.microsoft.icon",
"image/x-icon",
"text/css",
"text/html",
"text/javascript",
"text/plain",
"text/xml"
)
mimetype.assign = ( mimetype.assign = (
".ico" => "image/x-icon", ".ico" => "image/x-icon",
".jpeg" => "image/jpeg", ".jpeg" => "image/jpeg",
@ -82,6 +61,10 @@ mimetype.assign = (
".woff2" => "font/woff2" ".woff2" => "font/woff2"
) )
# Add user chosen options held in external file
# This uses include_shell instead of an include wildcard for compatibility
include_shell "cat external.conf 2>/dev/null"
# default listening port for IPv6 falls back to the IPv4 port # default listening port for IPv6 falls back to the IPv4 port
#include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port #include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port
#include_shell "/usr/share/lighttpd/create-mime.assign.pl" #include_shell "/usr/share/lighttpd/create-mime.assign.pl"
@ -103,11 +86,6 @@ $HTTP["url"] =~ "^/admin/" {
"X-Pi-hole" => "The Pi-hole Web interface is working!", "X-Pi-hole" => "The Pi-hole Web interface is working!",
"X-Frame-Options" => "DENY" "X-Frame-Options" => "DENY"
) )
$HTTP["url"] =~ "\.(eot|otf|tt[cf]|woff2?)$" {
# Allow Block Page access to local fonts
setenv.add-response-header = ( "Access-Control-Allow-Origin" => "*" )
}
} }
# Block . files from being served, such as .git, .github, .gitignore # Block . files from being served, such as .git, .github, .gitignore
@ -115,9 +93,12 @@ $HTTP["url"] =~ "^/admin/\.(.*)" {
url.access-deny = ("") url.access-deny = ("")
} }
# allow teleporter iframe on settings page
$HTTP["url"] =~ "/teleporter\.php$" {
$HTTP["referer"] =~ "/admin/settings\.php" {
setenv.add-response-header = ( "X-Frame-Options" => "SAMEORIGIN" )
}
}
# Default expire header # Default expire header
expire.url = ( "" => "access plus 0 seconds" ) expire.url = ( "" => "access plus 0 seconds" )
# Add user chosen options held in external file
# This uses include_shell instead of an include wildcard for compatibility
include_shell "cat external.conf 2>/dev/null"

File diff suppressed because it is too large Load diff

View file

@ -31,7 +31,7 @@ else
else else
echo -e " ${CROSS} ${str} echo -e " ${CROSS} ${str}
Script called with non-root privileges Script called with non-root privileges
The Pi-hole requires elevated privleges to uninstall" The Pi-hole requires elevated privileges to uninstall"
exit 1 exit 1
fi fi
fi fi
@ -42,8 +42,8 @@ source "${PI_HOLE_FILES_DIR}/automated install/basic-install.sh"
# setupVars set in basic-install.sh # setupVars set in basic-install.sh
source "${setupVars}" source "${setupVars}"
# distro_check() sourced from basic-install.sh # package_manager_detect() sourced from basic-install.sh
distro_check package_manager_detect
# Install packages used by the Pi-hole # Install packages used by the Pi-hole
DEPS=("${INSTALLER_DEPS[@]}" "${PIHOLE_DEPS[@]}") DEPS=("${INSTALLER_DEPS[@]}" "${PIHOLE_DEPS[@]}")
@ -113,7 +113,7 @@ removeNoPurge() {
fi fi
fi fi
echo -e "${OVER} ${TICK} Removed Web Interface" echo -e "${OVER} ${TICK} Removed Web Interface"
# Attempt to preserve backwards compatibility with older versions # Attempt to preserve backwards compatibility with older versions
# to guarantee no additional changes were made to /etc/crontab after # to guarantee no additional changes were made to /etc/crontab after
# the installation of pihole, /etc/crontab.pihole should be permanently # the installation of pihole, /etc/crontab.pihole should be permanently
@ -145,6 +145,7 @@ removeNoPurge() {
${SUDO} rm -f /etc/dnsmasq.d/adList.conf &> /dev/null ${SUDO} rm -f /etc/dnsmasq.d/adList.conf &> /dev/null
${SUDO} rm -f /etc/dnsmasq.d/01-pihole.conf &> /dev/null ${SUDO} rm -f /etc/dnsmasq.d/01-pihole.conf &> /dev/null
${SUDO} rm -f /etc/dnsmasq.d/06-rfc6761.conf &> /dev/null
${SUDO} rm -rf /var/log/*pihole* &> /dev/null ${SUDO} rm -rf /var/log/*pihole* &> /dev/null
${SUDO} rm -rf /etc/pihole/ &> /dev/null ${SUDO} rm -rf /etc/pihole/ &> /dev/null
${SUDO} rm -rf /etc/.pihole/ &> /dev/null ${SUDO} rm -rf /etc/.pihole/ &> /dev/null
@ -206,11 +207,7 @@ removeNoPurge() {
} }
######### SCRIPT ########### ######### SCRIPT ###########
if command -v vcgencmd &> /dev/null; then echo -e " ${INFO} Be sure to confirm if any dependencies should not be removed"
echo -e " ${INFO} All dependencies are safe to remove on Raspbian"
else
echo -e " ${INFO} Be sure to confirm if any dependencies should not be removed"
fi
while true; do while true; do
echo -e " ${INFO} ${COL_YELLOW}The following dependencies may have been added by the Pi-hole install:" echo -e " ${INFO} ${COL_YELLOW}The following dependencies may have been added by the Pi-hole install:"
echo -n " " echo -n " "

View file

@ -15,8 +15,6 @@ export LC_ALL=C
coltable="/opt/pihole/COL_TABLE" coltable="/opt/pihole/COL_TABLE"
source "${coltable}" source "${coltable}"
regexconverter="/opt/pihole/wildcard_regex_converter.sh"
source "${regexconverter}"
# shellcheck disable=SC1091 # shellcheck disable=SC1091
source "/etc/.pihole/advanced/Scripts/database_migration/gravity-db.sh" source "/etc/.pihole/advanced/Scripts/database_migration/gravity-db.sh"
@ -35,8 +33,9 @@ localList="${piholeDir}/local.list"
VPNList="/etc/openvpn/ipp.txt" VPNList="/etc/openvpn/ipp.txt"
piholeGitDir="/etc/.pihole" piholeGitDir="/etc/.pihole"
gravityDBfile="${piholeDir}/gravity.db" gravityDBfile_default="${piholeDir}/gravity.db"
gravityTEMPfile="${piholeDir}/gravity_temp.db" # GRAVITYDB may be overwritten by source pihole-FTL.conf below
GRAVITYDB="${gravityDBfile_default}"
gravityDBschema="${piholeGitDir}/advanced/Templates/gravity.db.sql" gravityDBschema="${piholeGitDir}/advanced/Templates/gravity.db.sql"
gravityDBcopy="${piholeGitDir}/advanced/Templates/gravity_copy.sql" gravityDBcopy="${piholeGitDir}/advanced/Templates/gravity_copy.sql"
@ -46,16 +45,6 @@ domainsExtension="domains"
setupVars="${piholeDir}/setupVars.conf" setupVars="${piholeDir}/setupVars.conf"
if [[ -f "${setupVars}" ]];then if [[ -f "${setupVars}" ]];then
source "${setupVars}" source "${setupVars}"
# Remove CIDR mask from IPv4/6 addresses
IPV4_ADDRESS="${IPV4_ADDRESS%/*}"
IPV6_ADDRESS="${IPV6_ADDRESS%/*}"
# Determine if IPv4/6 addresses exist
if [[ -z "${IPV4_ADDRESS}" ]] && [[ -z "${IPV6_ADDRESS}" ]]; then
echo -e " ${COL_LIGHT_RED}No IP addresses found! Please run 'pihole -r' to reconfigure${COL_NC}"
exit 1
fi
else else
echo -e " ${COL_LIGHT_RED}Installation Failure: ${setupVars} does not exist! ${COL_NC} echo -e " ${COL_LIGHT_RED}Installation Failure: ${setupVars} does not exist! ${COL_NC}
Please run 'pihole -r', and choose the 'reconfigure' option to fix." Please run 'pihole -r', and choose the 'reconfigure' option to fix."
@ -68,6 +57,13 @@ if [[ -f "${pihole_FTL}" ]]; then
source "${pihole_FTL}" source "${pihole_FTL}"
fi fi
# Set this only after sourcing pihole-FTL.conf as the gravity database path may
# have changed
gravityDBfile="${GRAVITYDB}"
gravityTEMPfile="${GRAVITYDB}_temp"
gravityDIR="$(dirname -- "${gravityDBfile}")"
gravityOLDfile="${gravityDIR}/gravity_old.db"
if [[ -z "${BLOCKINGMODE}" ]] ; then if [[ -z "${BLOCKINGMODE}" ]] ; then
BLOCKINGMODE="NULL" BLOCKINGMODE="NULL"
fi fi
@ -84,11 +80,11 @@ generate_gravity_database() {
# Copy data from old to new database file and swap them # Copy data from old to new database file and swap them
gravity_swap_databases() { gravity_swap_databases() {
local str local str copyGravity
str="Building tree" str="Building tree"
echo -ne " ${INFO} ${str}..." echo -ne " ${INFO} ${str}..."
# The index is intentionally not UNIQUE as prro quality adlists may contain domains more than once # The index is intentionally not UNIQUE as poor quality adlists may contain domains more than once
output=$( { sqlite3 "${gravityTEMPfile}" "CREATE INDEX idx_gravity ON gravity (domain, adlist_id);"; } 2>&1 ) output=$( { sqlite3 "${gravityTEMPfile}" "CREATE INDEX idx_gravity ON gravity (domain, adlist_id);"; } 2>&1 )
status="$?" status="$?"
@ -101,7 +97,14 @@ gravity_swap_databases() {
str="Swapping databases" str="Swapping databases"
echo -ne " ${INFO} ${str}..." echo -ne " ${INFO} ${str}..."
output=$( { sqlite3 "${gravityTEMPfile}" < "${gravityDBcopy}"; } 2>&1 ) # Gravity copying SQL script
copyGravity="$(cat "${gravityDBcopy}")"
if [[ "${gravityDBfile}" != "${gravityDBfile_default}" ]]; then
# Replace default gravity script location by custom location
copyGravity="${copyGravity//"${gravityDBfile_default}"/"${gravityDBfile}"}"
fi
output=$( { sqlite3 "${gravityTEMPfile}" <<< "${copyGravity}"; } 2>&1 )
status="$?" status="$?"
if [[ "${status}" -ne 0 ]]; then if [[ "${status}" -ne 0 ]]; then
@ -110,8 +113,19 @@ gravity_swap_databases() {
fi fi
echo -e "${OVER} ${TICK} ${str}" echo -e "${OVER} ${TICK} ${str}"
# Swap databases and remove old database # Swap databases and remove or conditionally rename old database
rm "${gravityDBfile}" # Number of available blocks on disk
availableBlocks=$(stat -f --format "%a" "${gravityDIR}")
# Number of blocks, used by gravity.db
gravityBlocks=$(stat --format "%b" ${gravityDBfile})
# Only keep the old database if available disk space is at least twice the size of the existing gravity.db.
# Better be safe than sorry...
if [ "${availableBlocks}" -gt "$((gravityBlocks * 2))" ] && [ -f "${gravityDBfile}" ]; then
echo -e " ${TICK} The old database remains available."
mv "${gravityDBfile}" "${gravityOLDfile}"
else
rm "${gravityDBfile}"
fi
mv "${gravityTEMPfile}" "${gravityDBfile}" mv "${gravityTEMPfile}" "${gravityDBfile}"
} }
@ -176,7 +190,7 @@ database_table_from_file() {
echo "${rowid},\"${domain}\",${timestamp}" >> "${tmpFile}" echo "${rowid},\"${domain}\",${timestamp}" >> "${tmpFile}"
elif [[ "${table}" == "adlist" ]]; then elif [[ "${table}" == "adlist" ]]; then
# Adlist table format # Adlist table format
echo "${rowid},\"${domain}\",1,${timestamp},${timestamp},\"Migrated from ${source}\"," >> "${tmpFile}" echo "${rowid},\"${domain}\",1,${timestamp},${timestamp},\"Migrated from ${source}\",,0,0,0" >> "${tmpFile}"
else else
# White-, black-, and regexlist table format # White-, black-, and regexlist table format
echo "${rowid},${type},\"${domain}\",1,${timestamp},${timestamp},\"Migrated from ${source}\"" >> "${tmpFile}" echo "${rowid},${type},\"${domain}\",1,${timestamp},${timestamp},\"Migrated from ${source}\"" >> "${tmpFile}"
@ -217,6 +231,48 @@ database_adlist_updated() {
fi fi
} }
# Check if a column with name ${2} exists in gravity table with name ${1}
gravity_column_exists() {
output=$( { printf ".timeout 30000\\nSELECT EXISTS(SELECT * FROM pragma_table_info('%s') WHERE name='%s');\\n" "${1}" "${2}" | sqlite3 "${gravityDBfile}"; } 2>&1 )
if [[ "${output}" == "1" ]]; then
return 0 # Bash 0 is success
fi
return 1 # Bash non-0 is failure
}
# Update number of domain on this list. We store this in the "old" database as all values in the new database will later be overwritten
database_adlist_number() {
# Only try to set number of domains when this field exists in the gravity database
if ! gravity_column_exists "adlist" "number"; then
return;
fi
output=$( { printf ".timeout 30000\\nUPDATE adlist SET number = %i, invalid_domains = %i WHERE id = %i;\\n" "${num_lines}" "${num_invalid}" "${1}" | sqlite3 "${gravityDBfile}"; } 2>&1 )
status="$?"
if [[ "${status}" -ne 0 ]]; then
echo -e "\\n ${CROSS} Unable to update number of domains in adlist with ID ${1} in database ${gravityDBfile}\\n ${output}"
gravity_Cleanup "error"
fi
}
# Update status of this list. We store this in the "old" database as all values in the new database will later be overwritten
database_adlist_status() {
# Only try to set the status when this field exists in the gravity database
if ! gravity_column_exists "adlist" "status"; then
return;
fi
output=$( { printf ".timeout 30000\\nUPDATE adlist SET status = %i WHERE id = %i;\\n" "${2}" "${1}" | sqlite3 "${gravityDBfile}"; } 2>&1 )
status="$?"
if [[ "${status}" -ne 0 ]]; then
echo -e "\\n ${CROSS} Unable to update status of adlist with ID ${1} in database ${gravityDBfile}\\n ${output}"
gravity_Cleanup "error"
fi
}
# Migrate pre-v5.0 list files to database-based Pi-hole versions # Migrate pre-v5.0 list files to database-based Pi-hole versions
migrate_to_database() { migrate_to_database() {
# Create database file only if not present # Create database file only if not present
@ -317,6 +373,10 @@ gravity_CheckDNSResolutionAvailable() {
gravity_DownloadBlocklists() { gravity_DownloadBlocklists() {
echo -e " ${INFO} ${COL_BOLD}Neutrino emissions detected${COL_NC}..." echo -e " ${INFO} ${COL_BOLD}Neutrino emissions detected${COL_NC}..."
if [[ "${gravityDBfile}" != "${gravityDBfile_default}" ]]; then
echo -e " ${INFO} Storing gravity database in ${COL_BOLD}${gravityDBfile}${COL_NC}"
fi
# Retrieve source URLs from gravity database # Retrieve source URLs from gravity database
# We source only enabled adlists, sqlite3 stores boolean values as 0 (false) or 1 (true) # We source only enabled adlists, sqlite3 stores boolean values as 0 (false) or 1 (true)
mapfile -t sources <<< "$(sqlite3 "${gravityDBfile}" "SELECT address FROM vw_adlist;" 2> /dev/null)" mapfile -t sources <<< "$(sqlite3 "${gravityDBfile}" "SELECT address FROM vw_adlist;" 2> /dev/null)"
@ -363,7 +423,7 @@ gravity_DownloadBlocklists() {
target="$(mktemp -p "/tmp" --suffix=".gravity")" target="$(mktemp -p "/tmp" --suffix=".gravity")"
# Use compression to reduce the amount of data that is transfered # Use compression to reduce the amount of data that is transferred
# between the Pi-hole and the ad list provider. Use this feature # between the Pi-hole and the ad list provider. Use this feature
# only if it is supported by the locally available version of curl # only if it is supported by the locally available version of curl
if curl -V | grep -q "Features:.* libz"; then if curl -V | grep -q "Features:.* libz"; then
@ -444,6 +504,8 @@ gravity_DownloadBlocklists() {
} }
total_num=0 total_num=0
num_lines=0
num_invalid=0
parseList() { parseList() {
local adlistID="${1}" src="${2}" target="${3}" incorrect_lines local adlistID="${1}" src="${2}" target="${3}" incorrect_lines
# This sed does the following things: # This sed does the following things:
@ -454,7 +516,7 @@ parseList() {
# Find (up to) five domains containing invalid characters (see above) # Find (up to) five domains containing invalid characters (see above)
incorrect_lines="$(sed -e "/[^a-zA-Z0-9.\_-]/!d" "${src}" | head -n 5)" incorrect_lines="$(sed -e "/[^a-zA-Z0-9.\_-]/!d" "${src}" | head -n 5)"
local num_lines num_target_lines num_correct_lines num_invalid local num_target_lines num_correct_lines num_invalid
# Get number of lines in source file # Get number of lines in source file
num_lines="$(grep -c "^" "${src}")" num_lines="$(grep -c "^" "${src}")"
# Get number of lines in destination file # Get number of lines in destination file
@ -463,9 +525,9 @@ parseList() {
total_num="$num_target_lines" total_num="$num_target_lines"
num_invalid="$(( num_lines-num_correct_lines ))" num_invalid="$(( num_lines-num_correct_lines ))"
if [[ "${num_invalid}" -eq 0 ]]; then if [[ "${num_invalid}" -eq 0 ]]; then
echo " ${INFO} Received ${num_lines} domains" echo " ${INFO} Analyzed ${num_lines} domains"
else else
echo " ${INFO} Received ${num_lines} domains, ${num_invalid} domains invalid!" echo " ${INFO} Analyzed ${num_lines} domains, ${num_invalid} domains invalid!"
fi fi
# Display sample of invalid lines if we found some # Display sample of invalid lines if we found some
@ -476,11 +538,34 @@ parseList() {
done <<< "${incorrect_lines}" done <<< "${incorrect_lines}"
fi fi
} }
compareLists() {
local adlistID="${1}" target="${2}"
# Verify checksum when an older checksum exists
if [[ -s "${target}.sha1" ]]; then
if ! sha1sum --check --status --strict "${target}.sha1"; then
# The list changed upstream, we need to update the checksum
sha1sum "${target}" > "${target}.sha1"
echo " ${INFO} List has been updated"
database_adlist_status "${adlistID}" "1"
database_adlist_updated "${adlistID}"
else
echo " ${INFO} List stayed unchanged"
database_adlist_status "${adlistID}" "2"
fi
else
# No checksum available, create one for comparing on the next run
sha1sum "${target}" > "${target}.sha1"
# We assume here it was changed upstream
database_adlist_status "${adlistID}" "1"
database_adlist_updated "${adlistID}"
fi
}
# Download specified URL and perform checks on HTTP status and file content # Download specified URL and perform checks on HTTP status and file content
gravity_DownloadBlocklistFromUrl() { gravity_DownloadBlocklistFromUrl() {
local url="${1}" cmd_ext="${2}" agent="${3}" adlistID="${4}" saveLocation="${5}" target="${6}" compression="${7}" local url="${1}" cmd_ext="${2}" agent="${3}" adlistID="${4}" saveLocation="${5}" target="${6}" compression="${7}"
local heisenbergCompensator="" patternBuffer str httpCode success="" local heisenbergCompensator="" patternBuffer str httpCode success="" ip
# Create temp file to store content on disk instead of RAM # Create temp file to store content on disk instead of RAM
patternBuffer=$(mktemp -p "/tmp" --suffix=".phgpb") patternBuffer=$(mktemp -p "/tmp" --suffix=".phgpb")
@ -498,7 +583,10 @@ gravity_DownloadBlocklistFromUrl() {
blocked=false blocked=false
case $BLOCKINGMODE in case $BLOCKINGMODE in
"IP-NODATA-AAAA"|"IP") "IP-NODATA-AAAA"|"IP")
if [[ $(dig "${domain}" +short | grep "${IPV4_ADDRESS}" -c) -ge 1 ]]; then # Get IP address of this domain
ip="$(dig "${domain}" +short)"
# Check if this IP matches any IP of the system
if [[ -n "${ip}" && $(grep -Ec "inet(|6) ${ip}" <<< "$(ip a)") -gt 0 ]]; then
blocked=true blocked=true
fi;; fi;;
"NXDOMAIN") "NXDOMAIN")
@ -559,31 +647,49 @@ gravity_DownloadBlocklistFromUrl() {
esac;; esac;;
esac esac
local done="false"
# Determine if the blocklist was downloaded and saved correctly # Determine if the blocklist was downloaded and saved correctly
if [[ "${success}" == true ]]; then if [[ "${success}" == true ]]; then
if [[ "${httpCode}" == "304" ]]; then if [[ "${httpCode}" == "304" ]]; then
# Add domains to database table file # Add domains to database table file
parseList "${adlistID}" "${saveLocation}" "${target}" parseList "${adlistID}" "${saveLocation}" "${target}"
database_adlist_status "${adlistID}" "2"
database_adlist_number "${adlistID}"
done="true"
# Check if $patternbuffer is a non-zero length file # Check if $patternbuffer is a non-zero length file
elif [[ -s "${patternBuffer}" ]]; then elif [[ -s "${patternBuffer}" ]]; then
# Determine if blocklist is non-standard and parse as appropriate # Determine if blocklist is non-standard and parse as appropriate
gravity_ParseFileIntoDomains "${patternBuffer}" "${saveLocation}" gravity_ParseFileIntoDomains "${patternBuffer}" "${saveLocation}"
# Add domains to database table file # Add domains to database table file
parseList "${adlistID}" "${saveLocation}" "${target}" parseList "${adlistID}" "${saveLocation}" "${target}"
# Update date_updated field in gravity database table # Compare lists, are they identical?
database_adlist_updated "${adlistID}" compareLists "${adlistID}" "${saveLocation}"
# Update gravity database table (status and updated timestamp are set in
# compareLists)
database_adlist_number "${adlistID}"
done="true"
else else
# Fall back to previously cached list if $patternBuffer is empty # Fall back to previously cached list if $patternBuffer is empty
echo -e " ${INFO} Received empty file: ${COL_LIGHT_GREEN}using previously cached list${COL_NC}" echo -e " ${INFO} Received empty file"
fi fi
else fi
# Do we need to fall back to a cached list (if available)?
if [[ "${done}" != "true" ]]; then
# Determine if cached list has read permission # Determine if cached list has read permission
if [[ -r "${saveLocation}" ]]; then if [[ -r "${saveLocation}" ]]; then
echo -e " ${CROSS} List download failed: ${COL_LIGHT_GREEN}using previously cached list${COL_NC}" echo -e " ${CROSS} List download failed: ${COL_LIGHT_GREEN}using previously cached list${COL_NC}"
# Add domains to database table file # Add domains to database table file
parseList "${adlistID}" "${saveLocation}" "${target}" parseList "${adlistID}" "${saveLocation}" "${target}"
database_adlist_number "${adlistID}"
database_adlist_status "${adlistID}" "3"
else else
echo -e " ${CROSS} List download failed: ${COL_LIGHT_RED}no cached list available${COL_NC}" echo -e " ${CROSS} List download failed: ${COL_LIGHT_RED}no cached list available${COL_NC}"
# Manually reset these two numbers because we do not call parseList here
num_lines=0
num_invalid=0
database_adlist_number "${adlistID}"
database_adlist_status "${adlistID}" "4"
fi fi
fi fi
} }
@ -595,7 +701,7 @@ gravity_ParseFileIntoDomains() {
# Determine if we are parsing a consolidated list # Determine if we are parsing a consolidated list
#if [[ "${source}" == "${piholeDir}/${matterAndLight}" ]]; then #if [[ "${source}" == "${piholeDir}/${matterAndLight}" ]]; then
# Remove comments and print only the domain name # Remove comments and print only the domain name
# Most of the lists downloaded are already in hosts file format but the spacing/formating is not contiguous # Most of the lists downloaded are already in hosts file format but the spacing/formatting is not contiguous
# This helps with that and makes it easier to read # This helps with that and makes it easier to read
# It also helps with debugging so each stage of the script can be researched more in depth # It also helps with debugging so each stage of the script can be researched more in depth
# 1) Remove carriage returns # 1) Remove carriage returns
@ -683,43 +789,12 @@ gravity_ShowCount() {
gravity_Table_Count "vw_regex_whitelist" "regex whitelist filters" gravity_Table_Count "vw_regex_whitelist" "regex whitelist filters"
} }
# Parse list of domains into hosts format
gravity_ParseDomainsIntoHosts() {
awk -v ipv4="$IPV4_ADDRESS" -v ipv6="$IPV6_ADDRESS" '{
# Remove windows CR line endings
sub(/\r$/, "")
# Parse each line as "ipaddr domain"
if(ipv6 && ipv4) {
print ipv4" "$0"\n"ipv6" "$0
} else if(!ipv6) {
print ipv4" "$0
} else {
print ipv6" "$0
}
}' >> "${2}" < "${1}"
}
# Create "localhost" entries into hosts format # Create "localhost" entries into hosts format
gravity_generateLocalList() { gravity_generateLocalList() {
local hostname
if [[ -s "/etc/hostname" ]]; then
hostname=$(< "/etc/hostname")
elif command -v hostname &> /dev/null; then
hostname=$(hostname -f)
else
echo -e " ${CROSS} Unable to determine fully qualified domain name of host"
return 0
fi
echo -e "${hostname}\\npi.hole" > "${localList}.tmp"
# Empty $localList if it already exists, otherwise, create it # Empty $localList if it already exists, otherwise, create it
: > "${localList}" echo "### Do not modify this file, it will be overwritten by pihole -g" > "${localList}"
chmod 644 "${localList}" chmod 644 "${localList}"
gravity_ParseDomainsIntoHosts "${localList}.tmp" "${localList}"
# Add additional LAN hosts provided by OpenVPN (if available) # Add additional LAN hosts provided by OpenVPN (if available)
if [[ -f "${VPNList}" ]]; then if [[ -f "${VPNList}" ]]; then
awk -F, '{printf $2"\t"$1".vpn\n"}' "${VPNList}" >> "${localList}" awk -F, '{printf $2"\t"$1".vpn\n"}' "${VPNList}" >> "${localList}"
@ -788,6 +863,11 @@ for var in "$@"; do
esac esac
done done
# Remove OLD (backup) gravity file, if it exists
if [[ -f "${gravityOLDfile}" ]]; then
rm "${gravityOLDfile}"
fi
# Trap Ctrl-C # Trap Ctrl-C
gravity_Trap gravity_Trap

View file

@ -56,7 +56,7 @@ Available commands and options:
\fB-w, whitelist\fR [options] [<domain1> <domain2 ...>] \fB-w, whitelist\fR [options] [<domain1> <domain2 ...>]
.br .br
Adds or removes specified domain or domains tho the Whitelist Adds or removes specified domain or domains to the Whitelist
.br .br
\fB-b, blacklist\fR [options] [<domain1> <domain2 ...>] \fB-b, blacklist\fR [options] [<domain1> <domain2 ...>]
@ -153,7 +153,7 @@ Available commands and options:
.br .br
-r, --refresh Set update frequency (in seconds) -r, --refresh Set update frequency (in seconds)
.br .br
-e, --exit Output stats and exit witout refreshing -e, --exit Output stats and exit without refreshing
.br .br
\fB-g, updateGravity\fR \fB-g, updateGravity\fR

97
pihole
View file

@ -16,6 +16,7 @@ readonly PI_HOLE_SCRIPT_DIR="/opt/pihole"
# error due to modifying a readonly variable. # error due to modifying a readonly variable.
setupVars="/etc/pihole/setupVars.conf" setupVars="/etc/pihole/setupVars.conf"
PI_HOLE_BIN_DIR="/usr/local/bin" PI_HOLE_BIN_DIR="/usr/local/bin"
readonly FTL_PID_FILE="/run/pihole-FTL.pid"
readonly colfile="${PI_HOLE_SCRIPT_DIR}/COL_TABLE" readonly colfile="${PI_HOLE_SCRIPT_DIR}/COL_TABLE"
source "${colfile}" source "${colfile}"
@ -98,8 +99,25 @@ versionFunc() {
exit 0 exit 0
} }
# Get PID of main pihole-FTL process
getFTLPID() {
local pid
if [ -s "${FTL_PID_FILE}" ]; then
# -s: FILE exists and has a size greater than zero
pid="$(<"$FTL_PID_FILE")"
# Exploit prevention: unset the variable if there is malicious content
# Verify that the value read from the file is numeric
[[ "$pid" =~ [^[:digit:]] ]] && unset pid
fi
# If FTL is not running, or the PID file contains malicious stuff, substitute
# negative PID to signal this to the caller
echo "${pid:=-1}"
}
restartDNS() { restartDNS() {
local svcOption svc str output status local svcOption svc str output status pid icon
svcOption="${1:-restart}" svcOption="${1:-restart}"
# Determine if we should reload or restart # Determine if we should reload or restart
@ -108,17 +126,34 @@ restartDNS() {
# Note 1: This will NOT re-read any *.conf files # Note 1: This will NOT re-read any *.conf files
# Note 2: We cannot use killall here as it does # Note 2: We cannot use killall here as it does
# not know about real-time signals # not know about real-time signals
svc="pkill -RTMIN pihole-FTL" pid="$(getFTLPID)"
str="Reloading DNS lists" if [[ "$pid" -eq "-1" ]]; then
svc="true"
str="FTL is not running"
icon="${INFO}"
else
svc="kill -RTMIN ${pid}"
str="Reloading DNS lists"
icon="${TICK}"
fi
elif [[ "${svcOption}" =~ "reload" ]]; then elif [[ "${svcOption}" =~ "reload" ]]; then
# Reloading of the DNS cache has been requested # Reloading of the DNS cache has been requested
# Note: This will NOT re-read any *.conf files # Note: This will NOT re-read any *.conf files
svc="pkill -HUP pihole-FTL" pid="$(getFTLPID)"
str="Flushing DNS cache" if [[ "$pid" -eq "-1" ]]; then
svc="true"
str="FTL is not running"
icon="${INFO}"
else
svc="kill -HUP ${pid}"
str="Flushing DNS cache"
icon="${TICK}"
fi
else else
# A full restart has been requested # A full restart has been requested
svc="service pihole-FTL restart" svc="service pihole-FTL restart"
str="Restarting DNS server" str="Restarting DNS server"
icon="${TICK}"
fi fi
# Print output to Terminal, but not to Web Admin # Print output to Terminal, but not to Web Admin
@ -128,7 +163,7 @@ restartDNS() {
status="$?" status="$?"
if [[ "${status}" -eq 0 ]]; then if [[ "${status}" -eq 0 ]]; then
[[ -t 1 ]] && echo -e "${OVER} ${TICK} ${str}" [[ -t 1 ]] && echo -e "${OVER} ${icon} ${str}"
return 0 return 0
else else
[[ ! -t 1 ]] && local OVER="" [[ ! -t 1 ]] && local OVER=""
@ -207,7 +242,7 @@ Time:
echo "BLOCKING_ENABLED=true" >> "${setupVars}" echo "BLOCKING_ENABLED=true" >> "${setupVars}"
fi fi
restartDNS reload restartDNS reload-lists
echo -e "${OVER} ${TICK} ${str}" echo -e "${OVER} ${TICK} ${str}"
} }
@ -328,16 +363,13 @@ tailFunc() {
fi fi
echo -e " ${INFO} Press Ctrl-C to exit" echo -e " ${INFO} Press Ctrl-C to exit"
# Retrieve IPv4/6 addresses
source /etc/pihole/setupVars.conf
# Strip date from each line # Strip date from each line
# Color blocklist/blacklist/wildcard entries as red # Color blocklist/blacklist/wildcard entries as red
# Color A/AAAA/DHCP strings as white # Color A/AAAA/DHCP strings as white
# Color everything else as gray # Color everything else as gray
tail -f /var/log/pihole.log | sed -E \ tail -f /var/log/pihole.log | grep --line-buffered "${1}" | sed -E \
-e "s,($(date +'%b %d ')| dnsmasq\[[0-9]*\]),,g" \ -e "s,($(date +'%b %d ')| dnsmasq\[[0-9]*\]),,g" \
-e "s,(.*(blacklisted |gravity blocked ).* is (0.0.0.0|::|NXDOMAIN|${IPV4_ADDRESS%/*}|${IPV6_ADDRESS:-NULL}).*),${COL_RED}&${COL_NC}," \ -e "s,(.*(blacklisted |gravity blocked ).*),${COL_RED}&${COL_NC}," \
-e "s,.*(query\\[A|DHCP).*,${COL_NC}&${COL_NC}," \ -e "s,.*(query\\[A|DHCP).*,${COL_NC}&${COL_NC}," \
-e "s,.*,${COL_GRAY}&${COL_NC}," -e "s,.*,${COL_GRAY}&${COL_NC},"
exit 0 exit 0
@ -367,34 +399,24 @@ Branches:
} }
tricorderFunc() { tricorderFunc() {
local tricorder_token
if [[ ! -p "/dev/stdin" ]]; then if [[ ! -p "/dev/stdin" ]]; then
echo -e " ${INFO} Please do not call Tricorder directly" echo -e " ${INFO} Please do not call Tricorder directly"
exit 1 exit 1
fi fi
if ! (echo > /dev/tcp/tricorder.pi-hole.net/9998) >/dev/null 2>&1; then tricorder_token=$(curl --silent --fail --show-error --upload-file "-" https://tricorder.pi-hole.net/upload < /dev/stdin 2>&1)
echo -e " ${CROSS} Unable to connect to Pi-hole's Tricorder server" if [[ "${tricorder_token}" != "https://tricorder.pi-hole.net/"* ]]; then
exit 1 echo -e "${CROSS} uploading failed, contact Pi-hole support for assistance."
fi # Log curl error (if available)
if [ -n "${tricorder_token}" ]; then
if command -v openssl &> /dev/null; then echo -e "${INFO} Error message: ${COL_RED}${tricorder_token}${COL_NC}\\n"
openssl s_client -quiet -connect tricorder.pi-hole.net:9998 2> /dev/null < /dev/stdin tricorder_token=""
exit "$?" fi
else exit 1
echo -e " ${INFO} ${COL_YELLOW}Security Notice${COL_NC}: ${COL_WHITE}openssl${COL_NC} is not installed
Your debug log will be transmitted unencrypted via plain-text
There is a possibility that this could be intercepted by a third party
If you wish to cancel, press Ctrl-C to exit within 10 seconds"
secs="10"
while [[ "$secs" -gt "0" ]]; do
echo -ne "."
sleep 1
: $((secs--))
done
echo " "
nc tricorder.pi-hole.net 9999 < /dev/stdin
exit "$?"
fi fi
echo "Upload successful, your token is: ${COL_GREEN}${tricorder_token}${COL_NC}"
exit 0
} }
updateCheckFunc() { updateCheckFunc() {
@ -421,7 +443,10 @@ Debugging Options:
Add '-a' to automatically upload the log to tricorder.pi-hole.net Add '-a' to automatically upload the log to tricorder.pi-hole.net
-f, flush Flush the Pi-hole log -f, flush Flush the Pi-hole log
-r, reconfigure Reconfigure or Repair Pi-hole subsystems -r, reconfigure Reconfigure or Repair Pi-hole subsystems
-t, tail View the live output of the Pi-hole log -t, tail [arg] View the live output of the Pi-hole log.
Add an optional argument to filter the log
(regular expressions are supported)
Options: Options:
-a, admin Web interface options -a, admin Web interface options
@ -495,7 +520,7 @@ case "${1}" in
"status" ) statusFunc "$2";; "status" ) statusFunc "$2";;
"restartdns" ) restartDNS "$2";; "restartdns" ) restartDNS "$2";;
"-a" | "admin" ) webpageFunc "$@";; "-a" | "admin" ) webpageFunc "$@";;
"-t" | "tail" ) tailFunc;; "-t" | "tail" ) tailFunc "$2";;
"checkout" ) piholeCheckoutFunc "$@";; "checkout" ) piholeCheckoutFunc "$@";;
"tricorder" ) tricorderFunc;; "tricorder" ) tricorderFunc;;
"updatechecker" ) updateCheckFunc "$@";; "updatechecker" ) updateCheckFunc "$@";;

View file

@ -1,5 +0,0 @@
Raspbian=9,10
Ubuntu=16,18,20
Debian=9,10
Fedora=31,32
CentOS=7,8

View file

@ -12,5 +12,6 @@ RUN true && \
chmod +x $SCRIPTDIR/* chmod +x $SCRIPTDIR/*
ENV PH_TEST true ENV PH_TEST true
ENV OS_CHECK_DOMAIN_NAME dev-supportedos.pi-hole.net
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \ #sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \

View file

@ -12,5 +12,6 @@ RUN true && \
chmod +x $SCRIPTDIR/* chmod +x $SCRIPTDIR/*
ENV PH_TEST true ENV PH_TEST true
ENV OS_CHECK_DOMAIN_NAME dev-supportedos.pi-hole.net
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \ #sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \

View file

@ -12,5 +12,6 @@ RUN true && \
chmod +x $SCRIPTDIR/* chmod +x $SCRIPTDIR/*
ENV PH_TEST true ENV PH_TEST true
ENV OS_CHECK_DOMAIN_NAME dev-supportedos.pi-hole.net
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \ #sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \

View file

@ -0,0 +1,17 @@
FROM buildpack-deps:bullseye-scm
ENV GITDIR /etc/.pihole
ENV SCRIPTDIR /opt/pihole
RUN mkdir -p $GITDIR $SCRIPTDIR /etc/pihole
ADD . $GITDIR
RUN cp $GITDIR/advanced/Scripts/*.sh $GITDIR/gravity.sh $GITDIR/pihole $GITDIR/automated\ install/*.sh $SCRIPTDIR/
ENV PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR
RUN true && \
chmod +x $SCRIPTDIR/*
ENV PH_TEST true
ENV OS_CHECK_DOMAIN_NAME dev-supportedos.pi-hole.net
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \

View file

@ -12,5 +12,6 @@ RUN true && \
chmod +x $SCRIPTDIR/* chmod +x $SCRIPTDIR/*
ENV PH_TEST true ENV PH_TEST true
ENV OS_CHECK_DOMAIN_NAME dev-supportedos.pi-hole.net
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \ #sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \

View file

@ -1,4 +1,4 @@
FROM fedora:31 FROM fedora:33
ENV GITDIR /etc/.pihole ENV GITDIR /etc/.pihole
ENV SCRIPTDIR /opt/pihole ENV SCRIPTDIR /opt/pihole
@ -12,5 +12,6 @@ RUN true && \
chmod +x $SCRIPTDIR/* chmod +x $SCRIPTDIR/*
ENV PH_TEST true ENV PH_TEST true
ENV OS_CHECK_DOMAIN_NAME dev-supportedos.pi-hole.net
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \ #sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \

View file

@ -1,4 +1,4 @@
FROM fedora:32 FROM fedora:34
ENV GITDIR /etc/.pihole ENV GITDIR /etc/.pihole
ENV SCRIPTDIR /opt/pihole ENV SCRIPTDIR /opt/pihole
@ -12,5 +12,6 @@ RUN true && \
chmod +x $SCRIPTDIR/* chmod +x $SCRIPTDIR/*
ENV PH_TEST true ENV PH_TEST true
ENV OS_CHECK_DOMAIN_NAME dev-supportedos.pi-hole.net
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \ #sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \

View file

@ -12,5 +12,6 @@ RUN true && \
chmod +x $SCRIPTDIR/* chmod +x $SCRIPTDIR/*
ENV PH_TEST true ENV PH_TEST true
ENV OS_CHECK_DOMAIN_NAME dev-supportedos.pi-hole.net
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \ #sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \

View file

@ -12,5 +12,6 @@ RUN true && \
chmod +x $SCRIPTDIR/* chmod +x $SCRIPTDIR/*
ENV PH_TEST true ENV PH_TEST true
ENV OS_CHECK_DOMAIN_NAME dev-supportedos.pi-hole.net
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \ #sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \

View file

@ -13,5 +13,6 @@ RUN true && \
chmod +x $SCRIPTDIR/* chmod +x $SCRIPTDIR/*
ENV PH_TEST true ENV PH_TEST true
ENV OS_CHECK_DOMAIN_NAME dev-supportedos.pi-hole.net
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \ #sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \

View file

@ -0,0 +1,18 @@
FROM buildpack-deps:hirsute-scm
ENV GITDIR /etc/.pihole
ENV SCRIPTDIR /opt/pihole
RUN mkdir -p $GITDIR $SCRIPTDIR /etc/pihole
ADD . $GITDIR
RUN cp $GITDIR/advanced/Scripts/*.sh $GITDIR/gravity.sh $GITDIR/pihole $GITDIR/automated\ install/*.sh $SCRIPTDIR/
ENV PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR
ENV DEBIAN_FRONTEND=noninteractive
RUN true && \
chmod +x $SCRIPTDIR/*
ENV PH_TEST true
ENV OS_CHECK_DOMAIN_NAME dev-supportedos.pi-hole.net
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \

View file

@ -8,8 +8,6 @@ check_output = testinfra.get_backend(
SETUPVARS = { SETUPVARS = {
'PIHOLE_INTERFACE': 'eth99', 'PIHOLE_INTERFACE': 'eth99',
'IPV4_ADDRESS': '1.1.1.1',
'IPV6_ADDRESS': 'FE80::240:D0FF:FE48:4672',
'PIHOLE_DNS_1': '4.2.2.1', 'PIHOLE_DNS_1': '4.2.2.1',
'PIHOLE_DNS_2': '4.2.2.2' 'PIHOLE_DNS_2': '4.2.2.2'
} }

View file

@ -11,20 +11,20 @@ from .conftest import (
) )
def test_supported_operating_system(Pihole): def test_supported_package_manager(Pihole):
''' '''
confirm installer exists on unsupported distribution confirm installer exits when no supported package manager found
''' '''
# break supported package managers to emulate an unsupported distribution # break supported package managers
Pihole.run('rm -rf /usr/bin/apt-get') Pihole.run('rm -rf /usr/bin/apt-get')
Pihole.run('rm -rf /usr/bin/rpm') Pihole.run('rm -rf /usr/bin/rpm')
distro_check = Pihole.run(''' package_manager_detect = Pihole.run('''
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
distro_check package_manager_detect
''') ''')
expected_stdout = cross_box + ' OS distribution not supported' expected_stdout = cross_box + ' No supported package manager found'
assert expected_stdout in distro_check.stdout assert expected_stdout in package_manager_detect.stdout
# assert distro_check.rc == 1 # assert package_manager_detect.rc == 1
def test_setupVars_are_sourced_to_global_scope(Pihole): def test_setupVars_are_sourced_to_global_scope(Pihole):
@ -45,8 +45,6 @@ def test_setupVars_are_sourced_to_global_scope(Pihole):
# Currently debug test function only # Currently debug test function only
echo "Outputting sourced variables" echo "Outputting sourced variables"
echo "PIHOLE_INTERFACE=${PIHOLE_INTERFACE}" 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_1=${PIHOLE_DNS_1}"
echo "PIHOLE_DNS_2=${PIHOLE_DNS_2}" echo "PIHOLE_DNS_2=${PIHOLE_DNS_2}"
} }
@ -137,7 +135,7 @@ def test_update_package_cache_success_no_errors(Pihole):
''' '''
updateCache = Pihole.run(''' updateCache = Pihole.run('''
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
distro_check package_manager_detect
update_package_cache update_package_cache
''') ''')
expected_stdout = tick_box + ' Update local cache of available packages' expected_stdout = tick_box + ' Update local cache of available packages'
@ -152,7 +150,7 @@ def test_update_package_cache_failure_no_errors(Pihole):
mock_command('apt-get', {'update': ('', '1')}, Pihole) mock_command('apt-get', {'update': ('', '1')}, Pihole)
updateCache = Pihole.run(''' updateCache = Pihole.run('''
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
distro_check package_manager_detect
update_package_cache update_package_cache
''') ''')
expected_stdout = cross_box + ' Update local cache of available packages' expected_stdout = cross_box + ' Update local cache of available packages'
@ -359,7 +357,7 @@ def test_FTL_download_aarch64_no_errors(Pihole):
mock_command('whiptail', {'*': ('', '0')}, Pihole) mock_command('whiptail', {'*': ('', '0')}, Pihole)
Pihole.run(''' Pihole.run('''
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
distro_check package_manager_detect
install_dependent_packages ${INSTALLER_DEPS[@]} install_dependent_packages ${INSTALLER_DEPS[@]}
''') ''')
download_binary = Pihole.run(''' download_binary = Pihole.run('''
@ -423,10 +421,9 @@ def test_IPv6_only_link_local(Pihole):
) )
detectPlatform = Pihole.run(''' detectPlatform = Pihole.run('''
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
useIPv6dialog find_IPv6_information
''') ''')
expected_stdout = ('Unable to find IPv6 ULA/GUA address, ' expected_stdout = ('Unable to find IPv6 ULA/GUA address')
'IPv6 adblocking will not be enabled')
assert expected_stdout in detectPlatform.stdout assert expected_stdout in detectPlatform.stdout
@ -447,9 +444,9 @@ def test_IPv6_only_ULA(Pihole):
) )
detectPlatform = Pihole.run(''' detectPlatform = Pihole.run('''
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
useIPv6dialog find_IPv6_information
''') ''')
expected_stdout = 'Found IPv6 ULA address, using it for blocking IPv6 ads' expected_stdout = 'Found IPv6 ULA address'
assert expected_stdout in detectPlatform.stdout assert expected_stdout in detectPlatform.stdout
@ -470,9 +467,9 @@ def test_IPv6_only_GUA(Pihole):
) )
detectPlatform = Pihole.run(''' detectPlatform = Pihole.run('''
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
useIPv6dialog find_IPv6_information
''') ''')
expected_stdout = 'Found IPv6 GUA address, using it for blocking IPv6 ads' expected_stdout = 'Found IPv6 GUA address'
assert expected_stdout in detectPlatform.stdout assert expected_stdout in detectPlatform.stdout
@ -494,9 +491,9 @@ def test_IPv6_GUA_ULA_test(Pihole):
) )
detectPlatform = Pihole.run(''' detectPlatform = Pihole.run('''
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
useIPv6dialog find_IPv6_information
''') ''')
expected_stdout = 'Found IPv6 ULA address, using it for blocking IPv6 ads' expected_stdout = 'Found IPv6 ULA address'
assert expected_stdout in detectPlatform.stdout assert expected_stdout in detectPlatform.stdout
@ -518,56 +515,59 @@ def test_IPv6_ULA_GUA_test(Pihole):
) )
detectPlatform = Pihole.run(''' detectPlatform = Pihole.run('''
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
useIPv6dialog find_IPv6_information
''') ''')
expected_stdout = 'Found IPv6 ULA address, using it for blocking IPv6 ads' expected_stdout = 'Found IPv6 ULA address'
assert expected_stdout in detectPlatform.stdout assert expected_stdout in detectPlatform.stdout
def test_validate_ip_valid(Pihole): def test_validate_ip(Pihole):
''' '''
Given a valid IP address, valid_ip returns success Tests valid_ip for various IP addresses
''' '''
output = Pihole.run(''' def test_address(addr, success=True):
source /opt/pihole/basic-install.sh output = Pihole.run('''
valid_ip "192.168.1.1" source /opt/pihole/basic-install.sh
''') valid_ip "{addr}"
'''.format(addr=addr))
assert output.rc == 0 assert output.rc == 0 if success else 1
test_address('192.168.1.1')
def test_validate_ip_invalid_octet(Pihole): test_address('127.0.0.1')
''' test_address('255.255.255.255')
Given an invalid IP address (large octet), valid_ip returns an error test_address('255.255.255.256', False)
''' test_address('255.255.256.255', False)
test_address('255.256.255.255', False)
output = Pihole.run(''' test_address('256.255.255.255', False)
source /opt/pihole/basic-install.sh test_address('1092.168.1.1', False)
valid_ip "1092.168.1.1" test_address('not an IP', False)
''') test_address('8.8.8.8#', False)
test_address('8.8.8.8#0')
assert output.rc == 1 test_address('8.8.8.8#1')
test_address('8.8.8.8#42')
test_address('8.8.8.8#888')
def test_validate_ip_invalid_letters(Pihole): test_address('8.8.8.8#1337')
''' test_address('8.8.8.8#65535')
Given an invalid IP address (contains letters), valid_ip returns an error test_address('8.8.8.8#65536', False)
''' test_address('8.8.8.8#-1', False)
test_address('00.0.0.0', False)
output = Pihole.run(''' test_address('010.0.0.0', False)
source /opt/pihole/basic-install.sh test_address('001.0.0.0', False)
valid_ip "not an IP" test_address('0.0.0.0#00', False)
''') test_address('0.0.0.0#01', False)
test_address('0.0.0.0#001', False)
assert output.rc == 1 test_address('0.0.0.0#0001', False)
test_address('0.0.0.0#00001', False)
def test_os_check_fails(Pihole): def test_os_check_fails(Pihole):
''' Confirms install fails on unsupported OS ''' ''' Confirms install fails on unsupported OS '''
Pihole.run(''' Pihole.run('''
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
distro_check package_manager_detect
install_dependent_packages ${OS_CHECK_DEPS[@]}
install_dependent_packages ${INSTALLER_DEPS[@]} install_dependent_packages ${INSTALLER_DEPS[@]}
cat <<EOT > /etc/os-release cat <<EOT > /etc/os-release
ID=UnsupportedOS ID=UnsupportedOS
@ -586,7 +586,8 @@ def test_os_check_passes(Pihole):
''' Confirms OS meets the requirements ''' ''' Confirms OS meets the requirements '''
Pihole.run(''' Pihole.run('''
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
distro_check package_manager_detect
install_dependent_packages ${OS_CHECK_DEPS[@]}
install_dependent_packages ${INSTALLER_DEPS[@]} install_dependent_packages ${INSTALLER_DEPS[@]}
''') ''')
detectOS = Pihole.run(''' detectOS = Pihole.run('''
@ -595,3 +596,44 @@ def test_os_check_passes(Pihole):
''') ''')
expected_stdout = 'Supported OS detected' expected_stdout = 'Supported OS detected'
assert expected_stdout in detectOS.stdout assert expected_stdout in detectOS.stdout
def test_package_manager_has_installer_deps(Pihole):
''' Confirms OS is able to install the required packages for the installer'''
mock_command('whiptail', {'*': ('', '0')}, Pihole)
output = Pihole.run('''
source /opt/pihole/basic-install.sh
package_manager_detect
install_dependent_packages ${INSTALLER_DEPS[@]}
''')
assert 'No package' not in output.stdout # centos7 still exits 0...
assert output.rc == 0
def test_package_manager_has_pihole_deps(Pihole):
''' Confirms OS is able to install the required packages for Pi-hole '''
mock_command('whiptail', {'*': ('', '0')}, Pihole)
output = Pihole.run('''
source /opt/pihole/basic-install.sh
package_manager_detect
select_rpm_php
install_dependent_packages ${PIHOLE_DEPS[@]}
''')
assert 'No package' not in output.stdout # centos7 still exits 0...
assert output.rc == 0
def test_package_manager_has_web_deps(Pihole):
''' Confirms OS is able to install the required packages for web '''
mock_command('whiptail', {'*': ('', '0')}, Pihole)
output = Pihole.run('''
source /opt/pihole/basic-install.sh
package_manager_detect
select_rpm_php
install_dependent_packages ${PIHOLE_WEB_DEPS[@]}
''')
assert 'No package' not in output.stdout # centos7 still exits 0...
assert output.rc == 0

View file

@ -9,13 +9,14 @@ def test_php_upgrade_default_optout_centos_eq_7(Pihole):
''' '''
confirms the default behavior to opt-out of installing PHP7 from REMI confirms the default behavior to opt-out of installing PHP7 from REMI
''' '''
distro_check = Pihole.run(''' package_manager_detect = Pihole.run('''
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
distro_check package_manager_detect
select_rpm_php
''') ''')
expected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS. ' expected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS. '
'Deprecated PHP may be in use.') 'Deprecated PHP may be in use.')
assert expected_stdout in distro_check.stdout assert expected_stdout in package_manager_detect.stdout
remi_package = Pihole.package('remi-release') remi_package = Pihole.package('remi-release')
assert not remi_package.is_installed assert not remi_package.is_installed
@ -27,13 +28,14 @@ def test_php_upgrade_user_optout_centos_eq_7(Pihole):
''' '''
# Whiptail dialog returns Cancel for user prompt # Whiptail dialog returns Cancel for user prompt
mock_command('whiptail', {'*': ('', '1')}, Pihole) mock_command('whiptail', {'*': ('', '1')}, Pihole)
distro_check = Pihole.run(''' package_manager_detect = Pihole.run('''
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
distro_check package_manager_detect
select_rpm_php
''') ''')
expected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS. ' expected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS. '
'Deprecated PHP may be in use.') 'Deprecated PHP may be in use.')
assert expected_stdout in distro_check.stdout assert expected_stdout in package_manager_detect.stdout
remi_package = Pihole.package('remi-release') remi_package = Pihole.package('remi-release')
assert not remi_package.is_installed assert not remi_package.is_installed
@ -45,16 +47,17 @@ def test_php_upgrade_user_optin_centos_eq_7(Pihole):
''' '''
# Whiptail dialog returns Continue for user prompt # Whiptail dialog returns Continue for user prompt
mock_command('whiptail', {'*': ('', '0')}, Pihole) mock_command('whiptail', {'*': ('', '0')}, Pihole)
distro_check = Pihole.run(''' package_manager_detect = Pihole.run('''
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
distro_check package_manager_detect
select_rpm_php
''') ''')
assert 'opt-out' not in distro_check.stdout assert 'opt-out' not in package_manager_detect.stdout
expected_stdout = info_box + (' Enabling Remi\'s RPM repository ' expected_stdout = info_box + (' Enabling Remi\'s RPM repository '
'(https://rpms.remirepo.net)') '(https://rpms.remirepo.net)')
assert expected_stdout in distro_check.stdout assert expected_stdout in package_manager_detect.stdout
expected_stdout = tick_box + (' Remi\'s RPM repository has ' expected_stdout = tick_box + (' Remi\'s RPM repository has '
'been enabled for PHP7') 'been enabled for PHP7')
assert expected_stdout in distro_check.stdout assert expected_stdout in package_manager_detect.stdout
remi_package = Pihole.package('remi-release') remi_package = Pihole.package('remi-release')
assert remi_package.is_installed assert remi_package.is_installed

View file

@ -10,13 +10,14 @@ def test_php_upgrade_default_continue_centos_gte_8(Pihole):
confirms the latest version of CentOS continues / does not optout confirms the latest version of CentOS continues / does not optout
(should trigger on CentOS7 only) (should trigger on CentOS7 only)
''' '''
distro_check = Pihole.run(''' package_manager_detect = Pihole.run('''
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
distro_check package_manager_detect
select_rpm_php
''') ''')
unexpected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS.' unexpected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS.'
' Deprecated PHP may be in use.') ' Deprecated PHP may be in use.')
assert unexpected_stdout not in distro_check.stdout assert unexpected_stdout not in package_manager_detect.stdout
# ensure remi was not installed on latest CentOS # ensure remi was not installed on latest CentOS
remi_package = Pihole.package('remi-release') remi_package = Pihole.package('remi-release')
assert not remi_package.is_installed assert not remi_package.is_installed
@ -30,13 +31,14 @@ def test_php_upgrade_user_optout_skipped_centos_gte_8(Pihole):
''' '''
# Whiptail dialog returns Cancel for user prompt # Whiptail dialog returns Cancel for user prompt
mock_command('whiptail', {'*': ('', '1')}, Pihole) mock_command('whiptail', {'*': ('', '1')}, Pihole)
distro_check = Pihole.run(''' package_manager_detect = Pihole.run('''
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
distro_check package_manager_detect
select_rpm_php
''') ''')
unexpected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS.' unexpected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS.'
' Deprecated PHP may be in use.') ' Deprecated PHP may be in use.')
assert unexpected_stdout not in distro_check.stdout assert unexpected_stdout not in package_manager_detect.stdout
# ensure remi was not installed on latest CentOS # ensure remi was not installed on latest CentOS
remi_package = Pihole.package('remi-release') remi_package = Pihole.package('remi-release')
assert not remi_package.is_installed assert not remi_package.is_installed
@ -50,16 +52,17 @@ def test_php_upgrade_user_optin_skipped_centos_gte_8(Pihole):
''' '''
# Whiptail dialog returns Continue for user prompt # Whiptail dialog returns Continue for user prompt
mock_command('whiptail', {'*': ('', '0')}, Pihole) mock_command('whiptail', {'*': ('', '0')}, Pihole)
distro_check = Pihole.run(''' package_manager_detect = Pihole.run('''
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
distro_check package_manager_detect
select_rpm_php
''') ''')
assert 'opt-out' not in distro_check.stdout assert 'opt-out' not in package_manager_detect.stdout
unexpected_stdout = info_box + (' Enabling Remi\'s RPM repository ' unexpected_stdout = info_box + (' Enabling Remi\'s RPM repository '
'(https://rpms.remirepo.net)') '(https://rpms.remirepo.net)')
assert unexpected_stdout not in distro_check.stdout assert unexpected_stdout not in package_manager_detect.stdout
unexpected_stdout = tick_box + (' Remi\'s RPM repository has ' unexpected_stdout = tick_box + (' Remi\'s RPM repository has '
'been enabled for PHP7') 'been enabled for PHP7')
assert unexpected_stdout not in distro_check.stdout assert unexpected_stdout not in package_manager_detect.stdout
remi_package = Pihole.package('remi-release') remi_package = Pihole.package('remi-release')
assert not remi_package.is_installed assert not remi_package.is_installed

View file

@ -13,29 +13,31 @@ def test_release_supported_version_check_centos(Pihole):
''' '''
# modify /etc/redhat-release to mock an unsupported CentOS release # modify /etc/redhat-release to mock an unsupported CentOS release
Pihole.run('echo "CentOS Linux release 6.9" > /etc/redhat-release') Pihole.run('echo "CentOS Linux release 6.9" > /etc/redhat-release')
distro_check = Pihole.run(''' package_manager_detect = Pihole.run('''
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
distro_check package_manager_detect
select_rpm_php
''') ''')
expected_stdout = cross_box + (' CentOS 6 is not supported.') expected_stdout = cross_box + (' CentOS 6 is not supported.')
assert expected_stdout in distro_check.stdout assert expected_stdout in package_manager_detect.stdout
expected_stdout = 'Please update to CentOS release 7 or later' expected_stdout = 'Please update to CentOS release 7 or later'
assert expected_stdout in distro_check.stdout assert expected_stdout in package_manager_detect.stdout
def test_enable_epel_repository_centos(Pihole): def test_enable_epel_repository_centos(Pihole):
''' '''
confirms the EPEL package repository is enabled when installed on CentOS confirms the EPEL package repository is enabled when installed on CentOS
''' '''
distro_check = Pihole.run(''' package_manager_detect = Pihole.run('''
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
distro_check package_manager_detect
select_rpm_php
''') ''')
expected_stdout = info_box + (' Enabling EPEL package repository ' expected_stdout = info_box + (' Enabling EPEL package repository '
'(https://fedoraproject.org/wiki/EPEL)') '(https://fedoraproject.org/wiki/EPEL)')
assert expected_stdout in distro_check.stdout assert expected_stdout in package_manager_detect.stdout
expected_stdout = tick_box + ' Installed epel-release' expected_stdout = tick_box + ' Installed epel-release'
assert expected_stdout in distro_check.stdout assert expected_stdout in package_manager_detect.stdout
epel_package = Pihole.package('epel-release') epel_package = Pihole.package('epel-release')
assert epel_package.is_installed assert epel_package.is_installed
@ -51,13 +53,14 @@ def test_php_version_lt_7_detected_upgrade_default_optout_centos(Pihole):
default_centos_php_version = php_package.version.split('.')[0] default_centos_php_version = php_package.version.split('.')[0]
if int(default_centos_php_version) >= 7: # PHP7 is supported/recommended if int(default_centos_php_version) >= 7: # PHP7 is supported/recommended
pytest.skip("Test deprecated . Detected default PHP version >= 7") pytest.skip("Test deprecated . Detected default PHP version >= 7")
distro_check = Pihole.run(''' package_manager_detect = Pihole.run('''
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
distro_check package_manager_detect
select_rpm_php
''') ''')
expected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS. ' expected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS. '
'Deprecated PHP may be in use.') 'Deprecated PHP may be in use.')
assert expected_stdout in distro_check.stdout assert expected_stdout in package_manager_detect.stdout
remi_package = Pihole.package('remi-release') remi_package = Pihole.package('remi-release')
assert not remi_package.is_installed assert not remi_package.is_installed
@ -75,13 +78,14 @@ def test_php_version_lt_7_detected_upgrade_user_optout_centos(Pihole):
pytest.skip("Test deprecated . Detected default PHP version >= 7") pytest.skip("Test deprecated . Detected default PHP version >= 7")
# Whiptail dialog returns Cancel for user prompt # Whiptail dialog returns Cancel for user prompt
mock_command('whiptail', {'*': ('', '1')}, Pihole) mock_command('whiptail', {'*': ('', '1')}, Pihole)
distro_check = Pihole.run(''' package_manager_detect = Pihole.run('''
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
distro_check package_manager_detect
select_rpm_php
''') ''')
expected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS. ' expected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS. '
'Deprecated PHP may be in use.') 'Deprecated PHP may be in use.')
assert expected_stdout in distro_check.stdout assert expected_stdout in package_manager_detect.stdout
remi_package = Pihole.package('remi-release') remi_package = Pihole.package('remi-release')
assert not remi_package.is_installed assert not remi_package.is_installed
@ -99,20 +103,21 @@ def test_php_version_lt_7_detected_upgrade_user_optin_centos(Pihole):
pytest.skip("Test deprecated . Detected default PHP version >= 7") pytest.skip("Test deprecated . Detected default PHP version >= 7")
# Whiptail dialog returns Continue for user prompt # Whiptail dialog returns Continue for user prompt
mock_command('whiptail', {'*': ('', '0')}, Pihole) mock_command('whiptail', {'*': ('', '0')}, Pihole)
distro_check = Pihole.run(''' package_manager_detect = Pihole.run('''
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
distro_check package_manager_detect
select_rpm_php
install_dependent_packages PIHOLE_WEB_DEPS[@] install_dependent_packages PIHOLE_WEB_DEPS[@]
''') ''')
expected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS. ' expected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS. '
'Deprecated PHP may be in use.') 'Deprecated PHP may be in use.')
assert expected_stdout not in distro_check.stdout assert expected_stdout not in package_manager_detect.stdout
expected_stdout = info_box + (' Enabling Remi\'s RPM repository ' expected_stdout = info_box + (' Enabling Remi\'s RPM repository '
'(https://rpms.remirepo.net)') '(https://rpms.remirepo.net)')
assert expected_stdout in distro_check.stdout assert expected_stdout in package_manager_detect.stdout
expected_stdout = tick_box + (' Remi\'s RPM repository has ' expected_stdout = tick_box + (' Remi\'s RPM repository has '
'been enabled for PHP7') 'been enabled for PHP7')
assert expected_stdout in distro_check.stdout assert expected_stdout in package_manager_detect.stdout
remi_package = Pihole.package('remi-release') remi_package = Pihole.package('remi-release')
assert remi_package.is_installed assert remi_package.is_installed
updated_php_package = Pihole.package('php') updated_php_package = Pihole.package('php')

View file

@ -3,11 +3,12 @@ def test_epel_and_remi_not_installed_fedora(Pihole):
confirms installer does not attempt to install EPEL/REMI repositories confirms installer does not attempt to install EPEL/REMI repositories
on Fedora on Fedora
''' '''
distro_check = Pihole.run(''' package_manager_detect = Pihole.run('''
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
distro_check package_manager_detect
select_rpm_php
''') ''')
assert distro_check.stdout == '' assert package_manager_detect.stdout == ''
epel_package = Pihole.package('epel-release') epel_package = Pihole.package('epel-release')
assert not epel_package.is_installed assert not epel_package.is_installed

8
test/tox.debian_11.ini Normal file
View file

@ -0,0 +1,8 @@
[tox]
envlist = py37
[testenv]
whitelist_externals = docker
deps = -rrequirements.txt
commands = docker build -f _debian_11.Dockerfile -t pytest_pihole:test_container ../
pytest {posargs:-vv -n auto} ./test_automated_install.py

View file

@ -4,5 +4,5 @@ envlist = py37
[testenv] [testenv]
whitelist_externals = docker whitelist_externals = docker
deps = -rrequirements.txt deps = -rrequirements.txt
commands = docker build -f _fedora_31.Dockerfile -t pytest_pihole:test_container ../ commands = docker build -f _fedora_33.Dockerfile -t pytest_pihole:test_container ../
pytest {posargs:-vv -n auto} ./test_automated_install.py ./test_centos_fedora_common_support.py ./test_fedora_support.py pytest {posargs:-vv -n auto} ./test_automated_install.py ./test_centos_fedora_common_support.py ./test_fedora_support.py

View file

@ -4,5 +4,5 @@ envlist = py37
[testenv] [testenv]
whitelist_externals = docker whitelist_externals = docker
deps = -rrequirements.txt deps = -rrequirements.txt
commands = docker build -f _fedora_32.Dockerfile -t pytest_pihole:test_container ../ commands = docker build -f _fedora_34.Dockerfile -t pytest_pihole:test_container ../
pytest {posargs:-vv -n auto} ./test_automated_install.py ./test_centos_fedora_common_support.py ./test_fedora_support.py pytest {posargs:-vv -n auto} ./test_automated_install.py ./test_centos_fedora_common_support.py ./test_fedora_support.py

8
test/tox.ubuntu_21.ini Normal file
View file

@ -0,0 +1,8 @@
[tox]
envlist = py37
[testenv]
whitelist_externals = docker
deps = -rrequirements.txt
commands = docker build -f _ubuntu_21.Dockerfile -t pytest_pihole:test_container ../
pytest {posargs:-vv -n auto} ./test_automated_install.py