diff --git a/.editorconfig b/.editorconfig index ee415d1f..a50f2f70 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,4 +1,4 @@ -# EditorConfig is awesome: http://EditorConfig.org +# EditorConfig is awesome: https://editorconfig.org/ # top-most EditorConfig file root = true diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index bef9f73c..00000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,37 +0,0 @@ -**In raising this issue, I confirm the following:** `{please fill the checkboxes, e.g: [X]}` - -- [] I have read and understood the [contributors guide](https://github.com/pi-hole/pi-hole/blob/master/CONTRIBUTING.md). -- [] The issue I am reporting can be *replicated*. -- [] The issue I am reporting isn't a duplicate (see [FAQs](https://github.com/pi-hole/pi-hole/wiki/FAQs), [closed issues](https://github.com/pi-hole/pi-hole/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), and [open issues](https://github.com/pi-hole/pi-hole/issues)). - -**How familiar are you with the the source code relevant to this issue?:** - -`{Replace this with a number from 1 to 10. 1 being not familiar, and 10 being very familiar}` - ---- -**Expected behavior:** - -`{A detailed description of what you expect to see}` - -**Actual behavior:** - -`{A detailed description and/or screenshots of what you do see}` - -**Steps to reproduce:** - -`{Detailed steps of how we can reproduce this}` - -**Debug token provided by [uploading `pihole -d` log](https://discourse.pi-hole.net/t/the-pihole-command-with-examples/738#debug):** - -`{Alphanumeric token}` - -**Troubleshooting undertaken, and/or other relevant information:** - -`{Steps of what you have done to fix this}` - -> * `{Please delete this quoted section when opening your issue}` -> * You must follow the template instructions. Failure to do so will result in your issue being closed. -> * Please [submit any feature requests here](https://discourse.pi-hole.net/c/feature-requests), so it is votable and trackable by the community. -> * Please respect that Pi-hole is developed by volunteers, who can only reply in their spare time. -> * Detail helps us understand and resolve an issue quicker, but please ensure it's relevant. -> * _This template was created based on the work of [`udemy-dl`](https://github.com/nishad/udemy-dl/blob/master/LICENSE)._ diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index 7509e923..00000000 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,31 +0,0 @@ -**By submitting this pull request, I confirm the following:** -*please fill any appropriate checkboxes, e.g: [X]* - -- [ ] I have read and understood the [contributors guide](https://github.com/pi-hole/pi-hole/blob/master/CONTRIBUTING.md), as well as this entire template. -- [ ] I have made only one major change in my proposed changes. -- [ ] I have commented my proposed changes within the code. -- [ ] I have tested my proposed changes, and have included unit tests where possible. -- [ ] I am willing to help maintain this change if there are issues with it later. -- [ ] I give this submission freely and claim no ownership. -- [ ] It is compatible with the [EUPL 1.2 license](https://opensource.org/licenses/EUPL-1.1) -- [ ] I have squashed any insignificant commits. ([`git rebase`](http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html)) - -Please make sure you [Sign Off](https://docs.pi-hole.net/guides/github/how-to-signoff/) all commits. Pi-hole enforces the [DCO](https://docs.pi-hole.net/guides/github/contributing/). - ---- -**What does this PR aim to accomplish?:** -*A detailed description, screenshots (if necessary), as well as links to any relevant GitHub issues* - - -**How does this PR accomplish the above?:** -*A detailed description (such as a changelog) and screenshots (if necessary) of the implemented fix* - - -**What documentation changes (if any) are needed to support this PR?:** -*A detailed list of any necessary changes* - - ---- -* You must follow the template instructions. Failure to do so will result in your pull request being closed. -* Please respect that Pi-hole is developed by volunteers, who can only reply in their spare time. - diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..129caea4 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,25 @@ +name: Test Supported Distributions + +on: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + +jobs: + distro-test: + if: github.event.pull_request.draft == false + runs-on: ubuntu-latest + strategy: + matrix: + distro: [debian_9, debian_10, debian_11, ubuntu_16, ubuntu_18, ubuntu_20, ubuntu_21, centos_7, centos_8, fedora_32, fedora_33] + env: + DISTRO: ${{matrix.distro}} + steps: + - uses: actions/checkout@v1 + - name: Set up Python 3.7 + uses: actions/setup-python@v2 + with: + python-version: 3.7 + - name: Install dependencies + run: pip install -r test/requirements.txt + - name: Test with tox + run: tox -c test/tox.${DISTRO}.ini diff --git a/.stickler.yml b/.stickler.yml index ab98025e..8a2a1ce9 100644 --- a/.stickler.yml +++ b/.stickler.yml @@ -3,3 +3,4 @@ linters: shell: bash phpcs: flake8: + max-line-length: 120 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 274c28cb..00000000 --- a/.travis.yml +++ /dev/null @@ -1,12 +0,0 @@ -sudo: required -services: - - docker -language: python -python: - - "3.6" -install: - - pip install -r requirements.txt - -script: - # tox.ini handles setup, ordering of docker build first, and then run tests - - tox diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e32b500e..0dd22b42 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,8 +4,8 @@ Please read and understand the contribution guide before creating an issue or pu ## Etiquette -- Our goal for Pi-hole is **stability before features**. This means we focus on squashing critical bugs before adding new features. Often, we can do both in tandem, but bugs will take priority over a new feature. -- Pi-hole is open source and [powered by donations](https://pi-hole.net/donate/), and as such, we give our **free time** to build, maintain, and **provide user support** for this project. It would be extremely unfair for us to suffer abuse or anger for our hard work, so please take a moment to consider that. +- Our goal for Pi-hole is **stability before features**. This means we focus on squashing critical bugs before adding new features. Often, we can do both in tandem, but bugs will take priority over a new feature. +- Pi-hole is open source and [powered by donations](https://pi-hole.net/donate/), and as such, we give our **free time** to build, maintain, and **provide user support** for this project. It would be extremely unfair for us to suffer abuse or anger for our hard work, so please take a moment to consider that. - Please be considerate towards the developers and other users when raising issues or presenting pull requests. - Respect our decision(s), and do not be upset or abusive if your submission is not used. @@ -26,13 +26,87 @@ When requesting or submitting new features, first consider whether it might be u - Check the codebase to ensure that your feature doesn't already exist. - Check the pull requests to ensure that another person hasn't already submitted the feature or fix. -- Read and understand the [DCO guidelines](https://github.com/pi-hole/pi-hole/wiki/Contributing-to-the-project) for the project. +- Read and understand the [DCO guidelines](https://docs.pi-hole.net/guides/github/contributing/) for the project. ## Technical Requirements - Submit Pull Requests to the **development branch only**. - Before Submitting your Pull Request, merge `development` with your new branch and fix any conflicts. (Make sure you don't break anything in development!) -- Please use the [Google Style Guide for Shell](https://google.github.io/styleguide/shell.xml) for your code submission styles. +- Please use the [Google Style Guide for Shell](https://google.github.io/styleguide/shell.xml) for your code submission styles. - Commit Unix line endings. - Please use the Pi-hole brand: **Pi-hole** (Take a special look at the capitalized 'P' and a low 'h' with a hyphen) - (Optional fun) keep to the theme of Star Trek/black holes/gravity. + +## Forking and Cloning from GitHub to GitHub + +1. Fork to a repo under a namespace you control, or have permission to use, for example: `https://github.com///`. You can do this from the github.com website. +2. Clone `https://github.com///` with the tool of you choice. +3. To keep your fork in sync with our repo, add an upstream remote for pi-hole/pi-hole to your repo. + + ```bash + git remote add upstream https://github.com/pi-hole/pi-hole.git + ``` + +4. Checkout the `development` branch from your fork `https://github.com///`. +5. Create a topic/branch, based on the `development` branch code. *Bonus fun to keep to the theme of Star Trek/black holes/gravity.* +6. Make your changes and commit to your topic branch in your repo. +7. Rebase your commits and squash any insignificant commits. See the notes below for an example. +8. Merge `development` your branch and fix any conflicts. +9. Open a Pull Request to merge your topic branch into our repo's `development` branch. + +- Keep in mind the technical requirements from above. + +## Forking and Cloning from GitHub to other code hosting sites + +- Forking is a GitHub concept and cannot be done from GitHub to other git-based code hosting sites. However, those sites may be able to mirror a GitHub repo. + +1. To contribute from another code hosting site, you must first complete the steps above to fork our repo to a GitHub namespace you have permission to use, for example: `https://github.com///`. +2. Create a repo in your code hosting site, for example: `https://gitlab.com///` +3. Follow the instructions from your code hosting site to create a mirror between `https://github.com///` and `https://gitlab.com///`. +4. When you are ready to create a Pull Request (PR), follow the steps `(starting at step #6)` from [Forking and Cloning from GitHub to GitHub](#forking-and-cloning-from-github-to-github) and create the PR from `https://github.com///`. + +## Notes for squashing commits with rebase + +- To rebase your commits and squash previous commits, you can use: + + ```bash + git rebase -i your_topic_branch~(number of commits to combine) + ``` + +- For more details visit [gitready.com](http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html) + +1. The following would combine the last four commits in the branch `mytopic`. + + ```bash + git rebase -i mytopic~4 + ``` + +2. An editor window opens with the most recent commits indicated: (edit the commands to the left of the commit ID) + + ```gitattributes + pick 9dff55b2 existing commit comments + squash ebb1a730 existing commit comments + squash 07cc5b50 existing commit comments + reword 9dff55b2 existing commit comments + ``` + +3. Save and close the editor. The next editor window opens: (edit the new commit message). *If you select reword for a commit, an additional editor window will open for you to edit the comment.* + + ```bash + new commit comments + Signed-off-by: yourname + ``` + +4. Save and close the editor for the rebase process to execute. The terminal output should say something like the following: + + ```bash + Successfully rebased and updated refs/heads/mytopic. + ``` + +5. Once you have a successful rebase, and before you sync your local clone, you have to force push origin to update your repo: + + ```bash + git push -f origin + ``` + +6. Continue on from step #7 from [Forking and Cloning from GitHub to GitHub](#forking-and-cloning-from-github-to-github) diff --git a/README.md b/README.md index 358308c3..06f541f4 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,22 @@ + + +# +

-Pi-hole
-Network-wide ad blocking via your own Linux hardware
+ + Pi-hole + +
+ Network-wide ad blocking via your own Linux hardware

+ -The Pi-hole[®](https://pi-hole.net/trademark-rules-and-brand-guidelines/) 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](https://www.youtube.com/watch?v=vKWjx1AQYgs) +- **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 - **Responsive**: seamlessly speeds up the feel of everyday browsing by caching DNS queries -- **Lightweight**: runs smoothly with [minimal hardware and software requirements](https://discourse.pi-hole.net/t/hardware-software-requirements/273) +- **Lightweight**: runs smoothly with [minimal hardware and software requirements](https://docs.pi-hole.net/main/prerequisites/) - **Robust**: a command line interface that is quality assured for interoperability - **Insightful**: a beautiful responsive Web Interface dashboard to view and control your Pi-hole - **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 @@ -17,32 +25,35 @@ The Pi-hole[®](https://pi-hole.net/trademark-rules-and-brand-guidelines/) is a - **Free**: open source software which helps ensure _you_ are the sole person in control of your privacy ----- -[![Codacy Badge](https://api.codacy.com/project/badge/Grade/c558a0f8d7124c99b02b84f0f5564238)](https://www.codacy.com/app/Pi-hole/pi-hole?utm_source=github.com&utm_medium=referral&utm_content=pi-hole/pi-hole&utm_campaign=Badge_Grade) -[![Build Status](https://travis-ci.org/pi-hole/pi-hole.svg?branch=development)](https://travis-ci.org/pi-hole/pi-hole) -[![BountySource](https://www.bountysource.com/badge/tracker?tracker_id=3011939)](https://www.bountysource.com/trackers/3011939-pi-hole-pi-hole?utm_source=3011939&utm_medium=shield&utm_campaign=TRACKER_BADGE) ## One-Step Automated Install + Those who want to get started quickly and conveniently may install Pi-hole using the following command: -#### `curl -sSL https://install.pi-hole.net | bash` +### `curl -sSL https://install.pi-hole.net | bash` ## Alternative Install Methods -[Piping to `bash` is controversial](https://pi-hole.net/2016/07/25/curling-and-piping-to-bash), as it prevents you from [reading code that is about to run](https://github.com/pi-hole/pi-hole/blob/master/automated%20install/basic-install.sh) on your system. Therefore, we provide these alternative installation methods which allow code review before installation: + +Piping to `bash` is [controversial](https://pi-hole.net/2016/07/25/curling-and-piping-to-bash), as it prevents you from [reading code that is about to run](https://github.com/pi-hole/pi-hole/blob/master/automated%20install/basic-install.sh) on your system. Therefore, we provide these alternative installation methods which allow code review before installation: ### Method 1: Clone our repository and run -``` + +```bash git clone --depth 1 https://github.com/pi-hole/pi-hole.git Pi-hole cd "Pi-hole/automated install/" sudo bash basic-install.sh ``` ### Method 2: Manually download the installer and run -``` + +```bash wget -O basic-install.sh https://install.pi-hole.net 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 +## [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. @@ -53,160 +64,101 @@ As a last resort, you can always manually set each device to use Pi-hole as thei ----- ## 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. Make no mistake: **your support is absolutely vital to help keep us innovating!** -### Donations -Sending a donation using our links below is **extremely helpful** in offsetting a portion of our monthly expenses: +### [Donations](https://pi-hole.net/donate) -- PP Donate via PayPal
-- BTC [Bitcoin, Bitcoin Cash, Ethereum, Litecoin](https://commerce.coinbase.com/checkout/dd304d04-f324-4a77-931b-0db61c77a41b) +Sending a donation using our Sponsor Button is **extremely helpful** in offsetting a portion of our monthly expenses and rewarding our dedicated development team: ### Alternative support -If you'd rather not [donate](https://pi-hole.net/donate/) (_which is okay!_), there are other ways you can help support us: -- [Patreon](https://patreon.com/pihole) _Become a patron for rewards_ -- [Digital Ocean](http://www.digitalocean.com/?refcode=344d234950e1) _affiliate link_ + +If you'd rather not donate (_which is okay!_), there are other ways you can help support us: + +- [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_ - [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_ -- [DNS Made Easy](https://cp.dnsmadeeasy.com/u/133706) _affiliate link_ -- [Vultr](http://www.vultr.com/?ref=7190426) _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 ### Contributing via GitHub + 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. 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. -### Presentations about Pi-hole -Word-of-mouth continues to help our project grow immensely, and so we are helping make this easier for people. - -If you are going to be presenting Pi-hole at a conference, meetup or even a school project, [get in touch with us](https://pi-hole.net/2017/05/17/giving-a-presentation-on-pi-hole-contact-us-first-for-some-goodies-and-support/) so we can hook you up with free swag to hand out to your audience! - ----- ## Getting in touch with us -While we are primarily reachable on our Discourse User Forum, 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 a variety of social media outlets. + +**Please be sure to check the FAQ's** before starting a new discussion. Many user questions already have answers and can be solved without any additional assistance. + +- [Frequently Asked Questions](https://discourse.pi-hole.net/c/faqs) +- [Feature Requests](https://discourse.pi-hole.net/c/feature-requests?order=votes) +- [Reddit](https://www.reddit.com/r/pihole/) +- [Twitter](https://twitter.com/The_Pi_hole) ----- ## Breakdown of Features -### The Command Line Interface -The `pihole` 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`. -Pi-hole Blacklist Demo +### [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 + +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). + +### 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`. Some notable features include: -* [Whitelisting, Blacklisting and Wildcards](https://github.com/pi-hole/pi-hole/wiki/Core-Function-Breakdown#whitelisting-blacklisting-and-wildcards) -* [Debugging utility](https://github.com/pi-hole/pi-hole/wiki/Core-Function-Breakdown#debugger) -* [Viewing the live log file](https://github.com/pi-hole/pi-hole/wiki/Core-Function-Breakdown#tail) -* [Real-time Statistics via `ssh`](https://github.com/pi-hole/pi-hole/wiki/Core-Function-Breakdown#chronometer) or [your TFT LCD screen](http://www.amazon.com/exec/obidos/ASIN/B00ID39LM4/pihole09-20) -* [Updating Ad Lists](https://github.com/pi-hole/pi-hole/wiki/Core-Function-Breakdown#gravity) -* [Querying Ad Lists for blocked domains](https://github.com/pi-hole/pi-hole/wiki/Core-Function-Breakdown#query) -* [Enabling and Disabling Pi-hole](https://github.com/pi-hole/pi-hole/wiki/Core-Function-Breakdown#enable--disable) -* ... and *many* more! -You can read our [Core Feature Breakdown](https://github.com/pi-hole/pi-hole/wiki/Core-Function-Breakdown), as well as read up on [example usage](https://discourse.pi-hole.net/t/the-pihole-command-with-examples/738) for more information. +- [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) +- [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) +- [Querying Ad Lists for blocked domains](https://docs.pi-hole.net/core/pihole-command/#query) +- [Enabling and Disabling Pi-hole](https://docs.pi-hole.net/core/pihole-command/#enable-disable) +- ... and *many* more! + +You can read our [Core Feature Breakdown](https://docs.pi-hole.net/core/pihole-command/#pi-hole-core) for more information. ### The Web Interface Dashboard + 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 - Some notable features include: -* Mobile friendly interface -* Password protection -* Detailed graphs and doughnut charts -* Top lists of domains and clients -* A filterable and sortable query log -* Long Term Statistics to view data over user-defined time ranges -* The ability to easily manage and configure Pi-hole features -* ... and all the main features of the Command Line Interface! + +- Mobile friendly interface +- Password protection +- Detailed graphs and doughnut charts +- Top lists of domains and clients +- A filterable and sortable query log +- Long Term Statistics to view data over user-defined time ranges +- The ability to easily manage and configure Pi-hole features +- ... and all the main features of the Command Line Interface! There are several ways to [access the dashboard](https://discourse.pi-hole.net/t/how-do-i-access-pi-holes-dashboard-admin-interface/3168): -1. `http:///admin/` -2. `http://pi.hole/admin/` (when using Pi-hole as your DNS server) -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 out find [more details over here](https://discourse.pi-hole.net/t/pi-hole-api/1863). - ------ - -## The Origin Of Pi-hole -Pi-hole being an **advertising-aware DNS/Web server**, makes use of the following technologies: - -* [`dnsmasq`](http://www.thekelleys.org.uk/dnsmasq/doc.html) - a lightweight DNS and DHCP server -* [`curl`](https://curl.haxx.se) - A command line tool for transferring data with URL syntax -* [`lighttpd`](https://www.lighttpd.net) - web server designed and optimized for high performance -* [`php`](https://secure.php.net) - a popular general-purpose web scripting language -* [AdminLTE Dashboard](https://github.com/almasaeed2010/AdminLTE) - premium admin control panel based on Bootstrap 3.x - -While quite outdated at this point, [this original blog post about Pi-hole](https://jacobsalmela.com/2015/06/16/block-millions-ads-network-wide-with-a-raspberry-pi-hole-2-0/) goes into **great detail** about how Pi-hole was originally set up and how it works. Syntactically, it's no longer accurate, but the same basic principles and logic still apply to Pi-hole's current state. - ------ - -## Coverage -- [Lifehacker: Turn A Raspberry Pi Into An Ad Blocker With A Single Command](https://www.lifehacker.com.au/2015/02/turn-a-raspberry-pi-into-an-ad-blocker-with-a-single-command/) (February, 2015) -- [MakeUseOf: Adblock Everywhere: The Raspberry Pi-Hole Way](http://www.makeuseof.com/tag/adblock-everywhere-raspberry-pi-hole-way/) (March, 2015) -- [Catchpoint: Ad-Blocking on Apple iOS9: Valuing the End User Experience](http://blog.catchpoint.com/2015/09/14/ad-blocking-apple/) (September, 2015) -- [Security Now Netcast: Pi-hole](https://www.youtube.com/watch?v=p7-osq_y8i8&t=100m26s) (October, 2015) -- [TekThing: Raspberry Pi-Hole Makes Ads Disappear!](https://youtu.be/8Co59HU2gY0?t=2m) (December, 2015) -- [Foolish Tech Show](https://youtu.be/bYyena0I9yc?t=2m4s) (December, 2015) -- [Block Ads on All Home Devices for $53.18](https://medium.com/@robleathern/block-ads-on-all-home-devices-for-53-18-a5f1ec139693#.gj1xpgr5d) (December, 2015) -- [Pi-Hole for Ubuntu 14.04](http://www.boyter.org/2015/12/pi-hole-ubuntu-14-04/) (December, 2015) -- [MacObserver Podcast 585](https://www.macobserver.com/tmo/podcast/macgeekgab-585) (December, 2015) -- [The Defrag Show: Endoscope USB Camera, The Final [HoloLens] Vote, Adblock Pi and more](https://channel9.msdn.com/Shows/The-Defrag-Show/Defrag-Endoscope-USB-Camera-The-Final-HoloLens-Vote-Adblock-Pi-and-more?WT.mc_id=dlvr_twitter_ch9#time=20m39s) (January, 2016) -- [Adafruit: Pi-hole is a black hole for internet ads](https://blog.adafruit.com/2016/03/04/pi-hole-is-a-black-hole-for-internet-ads-piday-raspberrypi-raspberry_pi/) (March, 2016) -- [Digital Trends: 5 Fun, Easy Projects You Can Try With a $35 Raspberry Pi](https://youtu.be/QwrKlyC2kdM?t=1m42s) (March, 2016) -- [Adafruit: Raspberry Pi Quick Look at Pi Hole ad blocking server with Tony D](https://www.youtube.com/watch?v=eg4u2j1HYlI) (June, 2016) -- [Devacron: OrangePi Zero as an Ad-Block server with Pi-Hole](http://www.devacron.com/orangepi-zero-as-an-ad-block-server-with-pi-hole/) (December, 2016) -- [Linux Pro: The Hole Truth](http://www.linuxpromagazine.com/Issues/2017/200/The-sysadmin-s-daily-grind-Pi-hole) (July, 2017) -- [Adafruit: installing Pi-hole on a Pi Zero W](https://learn.adafruit.com/pi-hole-ad-blocker-with-pi-zero-w/install-pi-hole) (August, 2017) -- [CryptoAUSTRALIA: How We Tried 5 Privacy Focused Raspberry Pi Projects](https://blog.cryptoaustralia.org.au/2017/10/05/5-privacy-focused-raspberry-pi-projects/) (October, 2017) -- [CryptoAUSTRALIA: Pi-hole Workshop](https://blog.cryptoaustralia.org.au/2017/11/02/pi-hole-network-wide-ad-blocker/) (November, 2017) -- [Know How 355: Killing ads with a Raspberry Pi-Hole!](https://www.twit.tv/shows/know-how/episodes/355) (November, 2017) -- [Hobohouse: Block Advertising on your Network with Pi-hole and Raspberry Pi](https://hobo.house/2018/02/27/block-advertising-with-pi-hole-and-raspberry-pi/) (March, 2018) -- [Scott Helme: Securing DNS across all of my devices with Pi-Hole + DNS-over-HTTPS + 1.1.1.1](https://scotthelme.co.uk/securing-dns-across-all-of-my-devices-with-pihole-dns-over-https-1-1-1-1/) (April, 2018) -- [Scott Helme: Catching and dealing with naughty devices on my home network](https://scotthelme.co.uk/catching-naughty-devices-on-my-home-network/) (April, 2018) -- [Bloomberg Business Week: Brotherhood of the Ad blockers](https://www.bloomberg.com/news/features/2018-05-10/inside-the-brotherhood-of-pi-hole-ad-blockers) (May, 2018) -- [Software Engineering Daily: Interview with the creator of Pi-hole](https://softwareengineeringdaily.com/2018/05/29/pi-hole-ad-blocker-hardware-with-jacob-salmela/) (May, 2018) -- [Raspberry Pi: Block ads at home using Pi-hole and a Raspberry Pi](https://www.raspberrypi.org/blog/pi-hole-raspberry-pi/) (July, 2018) -- [Troy Hunt: Mmm... Pi-hole...](https://www.troyhunt.com/mmm-pi-hole/) (September, 2018) -- [PEBKAK Podcast: Interview With Jacob Salmela](https://www.jerseystudios.net/2018/10/11/150-pi-hole/) (October, 2018) - ------ - -## Pi-hole Projects -- [The Big Blocklist Collection](https://wally3k.github.io) -- [Pie in the Sky-Hole](https://dlaa.me/blog/post/skyhole) -- [Copernicus: Windows Tray Application](https://github.com/goldbattle/copernicus) -- [Magic Mirror with DNS Filtering](https://zonksec.com/blog/magic-mirror-dns-filtering/#dnssoftware) -- [Windows DNS Swapper](https://github.com/roots84/DNS-Swapper) +1. `http://pi.hole/admin/` (when using Pi-hole as your DNS server) +2. `http:///admin/` diff --git a/advanced/01-pihole.conf b/advanced/01-pihole.conf index 2c8b3749..02bc93bf 100644 --- a/advanced/01-pihole.conf +++ b/advanced/01-pihole.conf @@ -34,11 +34,9 @@ server=@DNS2@ interface=@INT@ -cache-size=10000 +cache-size=@CACHE_SIZE@ log-queries log-facility=/var/log/pihole.log -local-ttl=2 - log-async diff --git a/advanced/06-rfc6761.conf b/advanced/06-rfc6761.conf new file mode 100644 index 00000000..e03569e8 --- /dev/null +++ b/advanced/06-rfc6761.conf @@ -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/ diff --git a/advanced/Scripts/chronometer.sh b/advanced/Scripts/chronometer.sh index 7431c212..3f85bdfc 100755 --- a/advanced/Scripts/chronometer.sh +++ b/advanced/Scripts/chronometer.sh @@ -13,6 +13,7 @@ LC_NUMERIC=C # Retrieve stats from FTL engine pihole-FTL() { + local ftl_port LINE ftl_port=$(cat /run/pihole-FTL.port 2> /dev/null) if [[ -n "$ftl_port" ]]; then # Open connection to FTL @@ -20,12 +21,13 @@ pihole-FTL() { # Test if connection is open if { "true" >&3; } 2> /dev/null; then - # Send command to FTL - echo -e ">$1" >&3 + # Send command to FTL and ask to quit when finished + echo -e ">$1 >quit" >&3 - # Read input + # Read input until we received an empty string and the connection is + # closed read -r -t 1 LINE <&3 - until [[ ! $? ]] || [[ "$LINE" == *"EOM"* ]]; do + until [[ -z "${LINE}" ]] && [[ ! -t 3 ]]; do echo "$LINE" >&1 read -r -t 1 LINE <&3 done @@ -153,7 +155,7 @@ get_init_stats() { sys_throttle_raw=$(vgt=$(sudo vcgencmd get_throttled); echo "${vgt##*x}") - # Active Throttle Notice: http://bit.ly/2gnunOo + # Active Throttle Notice: https://bit.ly/2gnunOo if [[ "$sys_throttle_raw" != "0" ]]; then case "$sys_throttle_raw" in *0001) thr_type="${COL_YELLOW}Under Voltage";; @@ -228,15 +230,21 @@ get_sys_stats() { mapfile -t ph_ver_raw < <(pihole -v -c 2> /dev/null | sed -n 's/^.* v/v/p') if [[ -n "${ph_ver_raw[0]}" ]]; then ph_core_ver="${ph_ver_raw[0]}" - ph_lte_ver="${ph_ver_raw[1]}" - ph_ftl_ver="${ph_ver_raw[2]}" + if [[ ${#ph_ver_raw[@]} -eq 2 ]]; then + # AdminLTE not installed + ph_lte_ver="(not installed)" + ph_ftl_ver="${ph_ver_raw[1]}" + else + ph_lte_ver="${ph_ver_raw[1]}" + ph_ftl_ver="${ph_ver_raw[2]}" + fi else ph_core_ver="-1" fi sys_name=$(hostname) - [[ -n "$TEMPERATUREUNIT" ]] && temp_unit="$TEMPERATUREUNIT" || temp_unit="c" + [[ -n "$TEMPERATUREUNIT" ]] && temp_unit="${TEMPERATUREUNIT^^}" || temp_unit="C" # Get storage stats for partition mounted on / read -r -a disk_raw <<< "$(df -B1 / 2> /dev/null | awk 'END{ print $3,$2,$5 }')" @@ -490,10 +498,6 @@ chronoFunc() { printFunc " RAM usage: " "$ram_perc%" "$ram_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 printFunc "DHCP usage: " "$ph_dhcp_percent%" "$dhcp_info" fi @@ -551,7 +555,7 @@ Calculates stats and displays to an LCD Options: -j, --json Output stats as JSON formatted string -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" fi diff --git a/advanced/Scripts/database_migration/gravity-db.sh b/advanced/Scripts/database_migration/gravity-db.sh index 70090a3b..22f241dd 100644 --- a/advanced/Scripts/database_migration/gravity-db.sh +++ b/advanced/Scripts/database_migration/gravity-db.sh @@ -110,4 +110,16 @@ upgrade_gravityDB(){ sqlite3 "${database}" < "${scriptPath}/11_to_12.sql" version=12 fi + if [[ "$version" == "12" ]]; then + # Add column date_updated to adlist table + echo -e " ${INFO} Upgrading gravity database from version 12 to 13" + sqlite3 "${database}" < "${scriptPath}/12_to_13.sql" + version=13 + 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 } diff --git a/advanced/Scripts/database_migration/gravity/12_to_13.sql b/advanced/Scripts/database_migration/gravity/12_to_13.sql new file mode 100644 index 00000000..d16791d6 --- /dev/null +++ b/advanced/Scripts/database_migration/gravity/12_to_13.sql @@ -0,0 +1,18 @@ +.timeout 30000 + +PRAGMA FOREIGN_KEYS=OFF; + +BEGIN TRANSACTION; + +ALTER TABLE adlist ADD COLUMN date_updated INTEGER; + +DROP TRIGGER tr_adlist_update; + +CREATE TRIGGER tr_adlist_update AFTER UPDATE OF address,enabled,comment ON adlist + BEGIN + UPDATE adlist SET date_modified = (cast(strftime('%s', 'now') as int)) WHERE id = NEW.id; + END; + +UPDATE info SET value = 13 WHERE property = 'version'; + +COMMIT; \ No newline at end of file diff --git a/advanced/Scripts/database_migration/gravity/13_to_14.sql b/advanced/Scripts/database_migration/gravity/13_to_14.sql new file mode 100644 index 00000000..fa230865 --- /dev/null +++ b/advanced/Scripts/database_migration/gravity/13_to_14.sql @@ -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; \ No newline at end of file diff --git a/advanced/Scripts/list.sh b/advanced/Scripts/list.sh index 77a5dece..e213b014 100755 --- a/advanced/Scripts/list.sh +++ b/advanced/Scripts/list.sh @@ -1,4 +1,6 @@ #!/usr/bin/env bash +# shellcheck disable=SC1090 + # 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. @@ -9,9 +11,17 @@ # Please see LICENSE file for your rights under this license. # Globals -basename=pihole -piholeDir=/etc/"${basename}" -gravityDBfile="${piholeDir}/gravity.db" +piholeDir="/etc/pihole" +GRAVITYDB="${piholeDir}/gravity.db" +# 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}" reload=false addmode=true @@ -112,7 +122,7 @@ ProcessDomainList() { for dom in "${domList[@]}"; do # Format domain into regex filter if requested if [[ "${wildcard}" == true ]]; then - dom="(^|\\.)${dom//\./\\.}$" + dom="(\\.|^)${dom//\./\\.}$" fi # Logic: If addmode then add to desired list and remove from the other; @@ -231,7 +241,15 @@ Displaylist() { } NukeList() { - sqlite3 "${gravityDBfile}" "DELETE FROM domainlist WHERE type = ${typeId};" + count=$(sqlite3 "${gravityDBfile}" "SELECT COUNT(1) FROM domainlist WHERE type = ${typeId};") + listname="$(GetListnameFromTypeId "${typeId}")" + if [ "$count" -gt 0 ];then + sqlite3 "${gravityDBfile}" "DELETE FROM domainlist WHERE type = ${typeId};" + echo " ${TICK} Removed ${count} domain(s) from the ${listname}" + else + echo " ${INFO} ${listname} already empty. Nothing to do!" + fi + exit 0; } GetComment() { diff --git a/advanced/Scripts/pihole-reenable.sh b/advanced/Scripts/pihole-reenable.sh new file mode 100755 index 00000000..93ec3b95 --- /dev/null +++ b/advanced/Scripts/pihole-reenable.sh @@ -0,0 +1,23 @@ +#!/bin/bash +# Pi-hole: A black hole for Internet advertisements +# (c) 2020 Pi-hole, LLC (https://pi-hole.net) +# Network-wide ad blocking via your own hardware. +# +# This file is copyright under the latest version of the EUPL. +# Please see LICENSE file for your rights under this license. +# +# +# The pihole disable command has the option to set a specified time before +# blocking is automatically re-enabled. +# +# Present script is responsible for the sleep & re-enable part of the job and +# is automatically terminated if it is still running when pihole is enabled by +# other means. +# +# This ensures that pihole ends up in the correct state after a sequence of +# commands suchs as: `pihole disable 30s; pihole enable; pihole disable` + +readonly PI_HOLE_BIN_DIR="/usr/local/bin" + +sleep "${1}" +"${PI_HOLE_BIN_DIR}"/pihole enable diff --git a/advanced/Scripts/piholeARPTable.sh b/advanced/Scripts/piholeARPTable.sh index b6b552c9..66d05bf9 100755 --- a/advanced/Scripts/piholeARPTable.sh +++ b/advanced/Scripts/piholeARPTable.sh @@ -38,7 +38,7 @@ flushARP(){ # Truncate network_addresses table in pihole-FTL.db # 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 echo -e "${OVER} ${CROSS} Failed to truncate network_addresses table" echo " Database location: ${DBFILE}" diff --git a/advanced/Scripts/piholeCheckout.sh b/advanced/Scripts/piholeCheckout.sh index 1c1b16a4..4c0a4f40 100644 --- a/advanced/Scripts/piholeCheckout.sh +++ b/advanced/Scripts/piholeCheckout.sh @@ -166,12 +166,15 @@ checkout() { checkout_pull_branch "${webInterfaceDir}" "${2}" elif [[ "${1}" == "ftl" ]] ; then local path + local oldbranch path="${2}/${binary}" + oldbranch="$(pihole-FTL -b)" if check_download_exists "$path"; then echo " ${TICK} Branch ${2} exists" echo "${2}" > /etc/pihole/ftlbranch chmod 644 /etc/pihole/ftlbranch + echo -e " ${INFO} Switching to branch: \"${2}\" from \"${oldbranch}\"" FTLinstall "${binary}" restart_service pihole-FTL enable_service pihole-FTL diff --git a/advanced/Scripts/piholeDebug.sh b/advanced/Scripts/piholeDebug.sh index abf56153..d199b4f5 100755 --- a/advanced/Scripts/piholeDebug.sh +++ b/advanced/Scripts/piholeDebug.sh @@ -46,8 +46,9 @@ OBFUSCATED_PLACEHOLDER="" # FAQ URLs for use in showing the debug log FAQ_UPDATE_PI_HOLE="${COL_CYAN}https://discourse.pi-hole.net/t/how-do-i-update-pi-hole/249${COL_NC}" FAQ_CHECKOUT_COMMAND="${COL_CYAN}https://discourse.pi-hole.net/t/the-pihole-command-with-examples/738#checkout${COL_NC}" -FAQ_HARDWARE_REQUIREMENTS="${COL_CYAN}https://discourse.pi-hole.net/t/hardware-software-requirements/273${COL_NC}" -FAQ_HARDWARE_REQUIREMENTS_PORTS="${COL_CYAN}https://discourse.pi-hole.net/t/hardware-software-requirements/273#ports${COL_NC}" +FAQ_HARDWARE_REQUIREMENTS="${COL_CYAN}https://docs.pi-hole.net/main/prerequisites/${COL_NC}" +FAQ_HARDWARE_REQUIREMENTS_PORTS="${COL_CYAN}https://docs.pi-hole.net/main/prerequisites/#ports${COL_NC}" +FAQ_HARDWARE_REQUIREMENTS_FIREWALLD="${COL_CYAN}https://docs.pi-hole.net/main/prerequisites/#firewalld${COL_NC}" FAQ_GATEWAY="${COL_CYAN}https://discourse.pi-hole.net/t/why-is-a-default-gateway-important-for-pi-hole/3546${COL_NC}" FAQ_ULA="${COL_CYAN}https://discourse.pi-hole.net/t/use-ipv6-ula-addresses-for-pi-hole/2127${COL_NC}" FAQ_FTL_COMPATIBILITY="${COL_CYAN}https://github.com/pi-hole/FTL#compatibility-list${COL_NC}" @@ -55,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 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 # https://discourse.pi-hole.net/t/what-files-does-pi-hole-use/1684 @@ -77,15 +73,12 @@ HTML_DIRECTORY="/var/www/html" WEB_GIT_DIRECTORY="${HTML_DIRECTORY}/admin" #BLOCK_PAGE_DIRECTORY="${HTML_DIRECTORY}/pihole" SHM_DIRECTORY="/dev/shm" +ETC="/etc" # Files required by Pi-hole # https://discourse.pi-hole.net/t/what-files-does-pi-hole-use/1684 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_CUSTOM_CONFIG_FILE="${WEB_SERVER_CONFIG_DIRECTORY}/external.conf" @@ -124,6 +117,8 @@ get_ftl_conf_value() { PIHOLE_GRAVITY_DB_FILE="$(get_ftl_conf_value "GRAVITYDB" "${PIHOLE_DIRECTORY}/gravity.db")" +PIHOLE_FTL_DB_FILE="$(get_ftl_conf_value "DBFILE" "${PIHOLE_DIRECTORY}/pihole-FTL.db")" + PIHOLE_COMMAND="${BIN_DIRECTORY}/pihole" PIHOLE_COLTABLE_FILE="${BIN_DIRECTORY}/COL_TABLE" @@ -138,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_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 # We can loop through the array at any time to see if it matches a value #SUPPORTED_OS=("Raspbian" "Ubuntu" "Fedora" "Debian" "CentOS") @@ -162,9 +160,6 @@ PIHOLE_PROCESSES=( "lighttpd" "pihole-FTL" ) # Store the required directories in an array so it can be parsed through REQUIRED_FILES=("${PIHOLE_CRON_FILE}" -"${PIHOLE_DNS_CONFIG_FILE}" -"${PIHOLE_DHCP_CONFIG_FILE}" -"${PIHOLE_WILDCARD_CONFIG_FILE}" "${WEB_SERVER_CONFIG_FILE}" "${WEB_SERVER_CUSTOM_CONFIG_FILE}" "${PIHOLE_INSTALL_LOG_FILE}" @@ -182,7 +177,9 @@ REQUIRED_FILES=("${PIHOLE_CRON_FILE}" "${PIHOLE_DEBUG_LOG}" "${PIHOLE_FTL_LOG}" "${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. @@ -232,6 +229,7 @@ copy_to_debug_log() { } initialize_debug() { + local system_uptime # Clear the screen so the debug log is readable clear show_disclaimer @@ -239,9 +237,13 @@ initialize_debug() { log_write "${COL_PURPLE}*** [ INITIALIZING ]${COL_NC}" # Timestamp the start of the log 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 # Colors do not show in the dasboard, but the icons do: [i], [✓], and [✗] echo_current_diagnostic() { @@ -302,7 +304,7 @@ compare_local_version_to_git_version() { remotes=$(git remote -v) log_write "${INFO} Remotes: ${remotes//$'\n'/'\n '}" - # If the repo is on the master branchs, they are on the stable codebase + # If the repo is on the master branch, they are on the stable codebase if [[ "${remote_branch}" == "master" ]]; then # so the color of the text is green log_write "${INFO} Branch: ${COL_GREEN}${remote_branch}${COL_NC}" @@ -315,7 +317,7 @@ compare_local_version_to_git_version() { log_write "${INFO} Commit: ${remote_commit}" # if `local_status` is non-null, then the repo is not clean, display details here if [[ ${local_status} ]]; then - #Replace new lines in the status with 12 spaces to make the output cleaner + # Replace new lines in the status with 12 spaces to make the output cleaner log_write "${INFO} Status: ${local_status//$'\n'/'\n '}" local local_diff local_diff=$(git diff) @@ -331,7 +333,17 @@ compare_local_version_to_git_version() { return 1 fi else - : + # There is no git directory so check if the web interface was disabled + local setup_vars_web_interface + setup_vars_web_interface=$(< ${PIHOLE_SETUP_VARS_FILE} grep ^INSTALL_WEB_INTERFACE | cut -d '=' -f2) + if [[ "${pihole_component}" == "Web" ]] && [[ "${setup_vars_web_interface}" == "false" ]]; then + log_write "${INFO} ${pihole_component}: Disabled in setupVars.conf via INSTALL_WEB_INTERFACE=false" + else + # Return an error message + log_write "${COL_RED}Directory ${git_dir} doesn't exist${COL_NC}" + # and exit with a non zero code + return 1 + fi fi } @@ -366,11 +378,11 @@ get_program_version() { # Create a local variable so this function can be safely reused local program_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 - "lighttpd") program_version="$(${program_name} -v |& 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)" ;; - "php") program_version="$(${program_name} -v |& head -n1 | cut -d '-' -f1 | cut -d ' ' -f2)" + "php") program_version="$(${program_name} -v 2> /dev/null | head -n1 | cut -d '-' -f1 | cut -d ' ' -f2)" ;; # If a match is not found, show an error *) echo "Unrecognized program"; @@ -393,53 +405,58 @@ check_critical_program_versions() { get_program_version "php" } -is_os_supported() { - local os_to_check="${1}" - # Strip just the base name of the system using sed - # shellcheck disable=SC2001 - the_os=$(echo "${os_to_check}" | sed 's/ .*//') - # If the variable is one of our supported OSes, - case "${the_os}" in - # Print it in green - "Raspbian") log_write "${TICK} ${COL_GREEN}${os_to_check}${COL_NC}";; - "Ubuntu") log_write "${TICK} ${COL_GREEN}${os_to_check}${COL_NC}";; - "Fedora") log_write "${TICK} ${COL_GREEN}${os_to_check}${COL_NC}";; - "Debian") log_write "${TICK} ${COL_GREEN}${os_to_check}${COL_NC}";; - "CentOS") log_write "${TICK} ${COL_GREEN}${os_to_check}${COL_NC}";; - # If not, show it in red and link to our software requirements page - *) log_write "${CROSS} ${COL_RED}${os_to_check}${COL_NC} (${FAQ_HARDWARE_REQUIREMENTS})"; - esac -} +os_check() { + # 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 + local remote_os_domain valid_os valid_version detected_os detected_version cmdResult digReturnCode response + remote_os_domain=${OS_CHECK_DOMAIN_NAME:-"versions.pi-hole.net"} -get_distro_attributes() { - # Put the current Internal Field Separator into another variable so it can be restored later - OLD_IFS="$IFS" - # Store the distro info in an array and make it global since the OS won't change, - # but we'll keep it within the function for better unit testing - local distro_info - #shellcheck disable=SC2016 - IFS=$'\r\n' command eval 'distro_info=( $(cat /etc/*release) )' + 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 '"') - # Set a named variable for better readability - local distro_attribute - # For each line found in an /etc/*release file, - for distro_attribute in "${distro_info[@]}"; do - # store the key in a variable - local pretty_name_key - pretty_name_key=$(echo "${distro_attribute}" | grep "PRETTY_NAME" | cut -d '=' -f1) - # we need just the OS PRETTY_NAME, - if [[ "${pretty_name_key}" == "PRETTY_NAME" ]]; then - # so save in in a variable when we find it - PRETTY_NAME_VALUE=$(echo "${distro_attribute}" | grep "PRETTY_NAME" | cut -d '=' -f2- | tr -d '"') - # then pass it as an argument that checks if the OS is supported - is_os_supported "${PRETTY_NAME_VALUE}" - else - # Since we only need the pretty name, we can just skip over anything that is not a match - : + 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) + digReturnCode="${cmdResult##*$'\n'}" + + # Extract dig response + response="${cmdResult%%$'\n'*}" + + IFS=" " read -r -a supportedOS < <(echo "${response}" | tr -d '"') + for distro_and_versions in "${supportedOS[@]}" + do + distro_part="${distro_and_versions%%=*}" + versions_part="${distro_and_versions##*=}" + + if [[ "${detected_os^^}" =~ ${distro_part^^} ]]; then + valid_os=true + IFS="," read -r -a supportedVer <<<"${versions_part}" + for version in "${supportedVer[@]}" + do + if [[ "${detected_version}" =~ $version ]]; then + valid_version=true + break + fi + done + break fi done - # Set the IFS back to what it was - IFS="$OLD_IFS" + + log_write "${INFO} dig return code: ${digReturnCode}" + log_write "${INFO} dig response: ${response}" + + if [ "$valid_os" = true ]; then + log_write "${TICK} Distro: ${COL_GREEN}${detected_os^}${COL_NC}" + + if [ "$valid_version" = true ]; then + log_write "${TICK} Version: ${COL_GREEN}${detected_version}${COL_NC}" + else + log_write "${CROSS} Version: ${COL_RED}${detected_version}${COL_NC}" + log_write "${CROSS} Error: ${COL_RED}${detected_os^} is supported but version ${detected_version} is currently unsupported (${FAQ_HARDWARE_REQUIREMENTS})${COL_NC}" + fi + else + log_write "${CROSS} Distro: ${COL_RED}${detected_os^}${COL_NC}" + log_write "${CROSS} Error: ${COL_RED}${detected_os^} is not a supported distro (${FAQ_HARDWARE_REQUIREMENTS})${COL_NC}" + fi } diagnose_operating_system() { @@ -451,7 +468,7 @@ diagnose_operating_system() { # If there is a /etc/*release file, it's probably a supported operating system, so we can if ls /etc/*release 1> /dev/null 2>&1; then # display the attributes to the user from the function made earlier - get_distro_attributes + os_check else # If it doesn't exist, it's not a system we currently support and link to FAQ log_write "${CROSS} ${COL_RED}${error_msg}${COL_NC} (${FAQ_HARDWARE_REQUIREMENTS})" @@ -488,6 +505,58 @@ check_selinux() { fi } +check_firewalld() { + # FirewallD ships by default on Fedora/CentOS/RHEL and enabled upon clean install + # FirewallD is not configured by the installer and is the responsibility of the user + echo_current_diagnostic "FirewallD" + # Check if FirewallD service is enabled + if command -v systemctl &> /dev/null; then + # get its status via systemctl + local firewalld_status + firewalld_status=$(systemctl is-active firewalld) + log_write "${INFO} ${COL_GREEN}Firewalld service ${firewalld_status}${COL_NC}"; + if [ "${firewalld_status}" == "active" ]; then + # test common required service ports + local firewalld_enabled_services + firewalld_enabled_services=$(firewall-cmd --list-services) + local firewalld_expected_services=("http" "dns" "dhcp" "dhcpv6") + for i in "${firewalld_expected_services[@]}"; do + if [[ "${firewalld_enabled_services}" =~ ${i} ]]; then + log_write "${TICK} ${COL_GREEN} Allow Service: ${i}${COL_NC}"; + else + log_write "${CROSS} ${COL_RED} Allow Service: ${i}${COL_NC} (${FAQ_HARDWARE_REQUIREMENTS_FIREWALLD})" + fi + done + # check for custom FTL FirewallD zone + local firewalld_zones + firewalld_zones=$(firewall-cmd --get-zones) + if [[ "${firewalld_zones}" =~ "ftl" ]]; then + log_write "${TICK} ${COL_GREEN}FTL Custom Zone Detected${COL_NC}"; + # check FTL custom zone interface: lo + local firewalld_ftl_zone_interfaces + firewalld_ftl_zone_interfaces=$(firewall-cmd --zone=ftl --list-interfaces) + if [[ "${firewalld_ftl_zone_interfaces}" =~ "lo" ]]; then + log_write "${TICK} ${COL_GREEN} Local Interface Detected${COL_NC}"; + else + log_write "${CROSS} ${COL_RED} Local Interface Not Detected${COL_NC} (${FAQ_HARDWARE_REQUIREMENTS_FIREWALLD})" + fi + # check FTL custom zone port: 4711 + local firewalld_ftl_zone_ports + firewalld_ftl_zone_ports=$(firewall-cmd --zone=ftl --list-ports) + if [[ "${firewalld_ftl_zone_ports}" =~ "4711/tcp" ]]; then + log_write "${TICK} ${COL_GREEN} FTL Port 4711/tcp Detected${COL_NC}"; + else + log_write "${CROSS} ${COL_RED} FTL Port 4711/tcp Not Detected${COL_NC} (${FAQ_HARDWARE_REQUIREMENTS_FIREWALLD})" + fi + else + log_write "${CROSS} ${COL_RED}FTL Custom Zone Not Detected${COL_NC} (${FAQ_HARDWARE_REQUIREMENTS_FIREWALLD})" + fi + fi + else + log_write "${TICK} ${COL_GREEN}Firewalld service not detected${COL_NC}"; + fi +} + processor_check() { echo_current_diagnostic "Processor" # Store the processor type in a variable @@ -500,7 +569,7 @@ processor_check() { else # Check if the architecture is currently supported for FTL case "${PROCESSOR}" in - "amd64") log_write "${TICK} ${COL_GREEN}${PROCESSOR}${COL_NC}" + "amd64" | "x86_64") log_write "${TICK} ${COL_GREEN}${PROCESSOR}${COL_NC}" ;; "armv6l") log_write "${TICK} ${COL_GREEN}${PROCESSOR}${COL_NC}" ;; @@ -535,43 +604,11 @@ parse_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() { # First argument should be a 4 or a 6 local protocol=${1} # 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 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) }') @@ -583,8 +620,7 @@ detect_ip_addresses() { 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 for i in "${!ip_addr_list[@]}"; do - # For each one in the list, print it out - does_ip_match_setup_vars "${protocol}" "${ip_addr_list[$i]}" + log_write " ${ip_addr_list[$i]}" done # Print a blank line just for formatting log_write "" @@ -593,13 +629,6 @@ detect_ip_addresses() { log_write "${CROSS} ${COL_RED}No IPv${protocol} address(es) found on the ${PIHOLE_INTERFACE}${COL_NC} interface.\\n" return 1 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() { @@ -625,7 +654,7 @@ ping_gateway() { # Check if we are using IPv4 or IPv6 # Find the default gateway using IPv4 or IPv6 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 [[ -n "${gateway}" ]]; then @@ -753,7 +782,7 @@ check_x_headers() { # Do it for the dashboard as well, as the header is different than above local dashboard 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 block_page_working="X-Pi-hole: A black hole for Internet advertisements." local dashboard_working @@ -772,12 +801,12 @@ check_x_headers() { log_write "${COL_RED}${full_curl_output_block_page}${COL_NC}" 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 # then we can show a success log_write "$TICK Web interface X-Header: ${COL_GREEN}${dashboard}${COL_NC}" 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 "${COL_RED}${full_curl_output_dashboard}${COL_NC}" fi @@ -789,13 +818,13 @@ dig_at() { # Store the arguments as variables with names local protocol="${1}" - local IP="${2}" echo_current_diagnostic "Name resolution (IPv${protocol}) using a random blocked domain and a known ad-serving domain" # Set more local variables # We need to test name resolution locally, via Pi-hole, and via a public resolver local local_dig - local pihole_dig local remote_dig + local interfaces + local addresses # 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 local remote_url="doubleclick.com" @@ -804,15 +833,15 @@ dig_at() { if [[ ${protocol} == "6" ]]; then # Set the IPv6 variables and record type local local_address="::1" - local pihole_address="${IP}" local remote_address="2001:4860:4860::8888" + local sed_selector="inet6" local record_type="AAAA" - # Othwerwise, it should be 4 + # Otherwise, it should be 4 else # so use the IPv4 values local local_address="127.0.0.1" - local pihole_address="${IP}" local remote_address="8.8.8.8" + local sed_selector="inet" local record_type="A" fi @@ -822,32 +851,55 @@ dig_at() { local random_url 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 # This better emulates how clients will interact with Pi-hole as opposed to above where Pi-hole is # 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) - if pihole_dig=$(dig +tries=1 +time=2 -"${protocol}" "${random_url}" @"${pihole_address}" +short "${record_type}"); then - # show a success - log_write "${TICK} ${random_url} ${COL_GREEN}is ${pihole_dig}${COL_NC} via ${COL_CYAN}Pi-hole${COL_NC} (${pihole_address})" - else - # Othewise, show a failure - log_write "${CROSS} ${COL_RED}Failed to resolve${COL_NC} ${random_url} via ${COL_RED}Pi-hole${COL_NC} (${pihole_address})" - fi + while IFS= read -r iface ; do + # Get addresses of current interface + # sed logic breakdown: + # /inet(|6) /!d; + # Removes all lines from ip a that do not contain either "inet " or "inet6 " + # s/^.*inet(|6) //g; + # Removes all leading whitespace as well as the "inet " or "inet6 " string + # 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 # 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 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 @@ -888,6 +940,18 @@ process_status(){ done } +ftl_full_status(){ + # if using systemd print the full status of pihole-FTL + echo_current_diagnostic "Pi-hole-FTL full status" + local FTL_status + if command -v systemctl &> /dev/null; then + FTL_status=$(systemctl status --full --no-pager pihole-FTL.service) + log_write " ${FTL_status}" + else + log_write "${INFO} systemctl: command not found" + fi +} + make_array_from_file() { local filename="${1}" # The second argument can put a limit on how many line should be read from the file @@ -905,7 +969,7 @@ make_array_from_file() { # Otherwise, read the file line by line while IFS= read -r line;do # Othwerise, strip out comments and blank lines - new_line=$(echo "${line}" | sed -e 's/#.*$//' -e '/^$/d') + new_line=$(echo "${line}" | sed -e 's/^\s*#.*$//' -e '/^$/d') # If the line still has content (a non-zero value) if [[ -n "${new_line}" ]]; then # Put it into the array @@ -950,7 +1014,7 @@ parse_file() { local file_lines # For each line in the file, for file_lines in "${file_info[@]}"; do - if [[ ! -z "${file_lines}" ]]; then + if [[ -n "${file_lines}" ]]; then # don't include the Web password hash [[ "${file_lines}" =~ ^\#.*$ || ! "${file_lines}" || "${file_lines}" == "WEBPASSWORD="* ]] && continue # otherwise, display the lines of the file @@ -962,14 +1026,10 @@ parse_file() { } 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 - dig_at 4 "${IPV4_ADDRESS%/*}" - # If IPv6 enabled, - if [[ "${IPV6_ADDRESS}" ]]; then - # check resolution - dig_at 6 "${IPV6_ADDRESS%/*}" - fi + dig_at 4 + dig_at 6 } # This function can check a directory exists @@ -1012,17 +1072,21 @@ list_files_in_dir() { : 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 - 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 # Then, parse the file's content into an array so each line can be analyzed if need be for i in "${!REQUIRED_FILES[@]}"; do if [[ "${dir_to_parse}/${each_file}" == "${REQUIRED_FILES[$i]}" ]]; then # 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) case "${dir_to_parse}/${each_file}" in - # If it's Web server error log, just give the first 25 lines - "${PIHOLE_WEB_SERVER_ERROR_LOG_FILE}") make_array_from_file "${dir_to_parse}/${each_file}" 25 + # If it's Web server error log, give the first and last 25 lines + "${PIHOLE_WEB_SERVER_ERROR_LOG_FILE}") head_tail_log "${dir_to_parse}/${each_file}" 25 ;; # Same for the FTL log "${PIHOLE_FTL_LOG}") head_tail_log "${dir_to_parse}/${each_file}" 35 @@ -1057,6 +1121,7 @@ show_content_of_pihole_files() { show_content_of_files_in_dir "${WEB_SERVER_LOG_DIRECTORY}" show_content_of_files_in_dir "${LOG_DIRECTORY}" show_content_of_files_in_dir "${SHM_DIRECTORY}" + show_content_of_files_in_dir "${ETC}" } head_tail_log() { @@ -1112,22 +1177,66 @@ show_db_entries() { IFS="$OLD_IFS" } +show_FTL_db_entries() { + local title="${1}" + local query="${2}" + local widths="${3}" + + echo_current_diagnostic "${title}" + + OLD_IFS="$IFS" + IFS=$'\r\n' + local entries=() + mapfile -t entries < <(\ + sqlite3 "${PIHOLE_FTL_DB_FILE}" \ + -cmd ".headers on" \ + -cmd ".mode column" \ + -cmd ".width ${widths}" \ + "${query}"\ + ) + + for line in "${entries[@]}"; do + log_write " ${line}" + done + + IFS="$OLD_IFS" +} + +check_dhcp_servers() { + echo_current_diagnostic "Discovering active DHCP servers (takes 10 seconds)" + + OLD_IFS="$IFS" + IFS=$'\n' + local entries=() + mapfile -t entries < <(pihole-FTL dhcp-discover) + + for line in "${entries[@]}"; do + log_write " ${line}" + done + + IFS="$OLD_IFS" +} + show_groups() { show_db_entries "Groups" "SELECT id,CASE enabled WHEN '0' THEN ' 0' WHEN '1' THEN ' 1' ELSE enabled END enabled,name,datetime(date_added,'unixepoch','localtime') date_added,datetime(date_modified,'unixepoch','localtime') date_modified,description FROM \"group\"" "4 7 50 19 19 50" } 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_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_db_entries "Clients" "SELECT id,GROUP_CONCAT(client_by_group.group_id) group_ids,ip,datetime(date_added,'unixepoch','localtime') date_added,datetime(date_modified,'unixepoch','localtime') date_modified,comment FROM client LEFT JOIN client_by_group ON client.id = client_by_group.client_id GROUP BY id;" "4 12 100 19 19 50" } +show_messages() { + show_FTL_db_entries "Pi-hole diagnosis messages" "SELECT id,datetime(timestamp,'unixepoch','localtime') timestamp,type,message,blob1,blob2,blob3,blob4,blob5 FROM message;" "4 19 20 60 20 20 20 20 20" +} + analyze_gravity_list() { echo_current_diagnostic "Gravity List and Database" @@ -1155,72 +1264,88 @@ analyze_gravity_list() { IFS="$OLD_IFS" } -analyze_pihole_log() { - echo_current_diagnostic "Pi-hole log" - local head_line - # 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' - local pihole_log_permissions - pihole_log_permissions=$(ls -ld "${PIHOLE_LOG}") - log_write "${COL_GREEN}${pihole_log_permissions}${COL_NC}" - local pihole_log_head=() - mapfile -t pihole_log_head < <(head -n 20 ${PIHOLE_LOG}) - log_write " ${COL_CYAN}-----head of $(basename ${PIHOLE_LOG})------${COL_NC}" - local error_to_check_for - local line_to_obfuscate - local obfuscated_line - for head_line in "${pihole_log_head[@]}"; do - # A common error in the pihole.log is when there is a non-hosts formatted file - # that the DNS server is attempting to read. Since it's not formatted - # correctly, there will be an entry for "bad address at line n" - # So we can check for that here and highlight it in red so the user can see it easily - error_to_check_for=$(echo "${head_line}" | grep 'bad address at') - # Some users may not want to have the domains they visit sent to us - # To that end, we check for lines in the log that would contain a domain name - line_to_obfuscate=$(echo "${head_line}" | grep ': query\|: forwarded\|: reply') - # If the variable contains a value, it found an error in the log - if [[ -n ${error_to_check_for} ]]; then - # So we can print it in red to make it visible to the user - log_write " ${CROSS} ${COL_RED}${head_line}${COL_NC} (${FAQ_BAD_ADDRESS})" - else - # If the variable does not a value (the current default behavior), so do not obfuscate anything - if [[ -z ${OBFUSCATE} ]]; then - log_write " ${head_line}" - # Othwerise, a flag was passed to this command to obfuscate domains in the log - else - # So first check if there are domains in the log that should be obfuscated - if [[ -n ${line_to_obfuscate} ]]; then - # 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" +obfuscated_pihole_log() { + local pihole_log=("$@") + local line + local error_to_check_for + local line_to_obfuscate + local obfuscated_line + for line in "${pihole_log[@]}"; do + # A common error in the pihole.log is when there is a non-hosts formatted file + # that the DNS server is attempting to read. Since it's not formatted + # correctly, there will be an entry for "bad address at line n" + # So we can check for that here and highlight it in red so the user can see it easily + error_to_check_for=$(echo "${line}" | grep 'bad address at') + # Some users may not want to have the domains they visit sent to us + # To that end, we check for lines in the log that would contain a domain name + line_to_obfuscate=$(echo "${line}" | grep ': query\|: forwarded\|: reply') + # If the variable contains a value, it found an error in the log + if [[ -n ${error_to_check_for} ]]; then + # So we can print it in red to make it visible to the user + log_write " ${CROSS} ${COL_RED}${line}${COL_NC} (${FAQ_BAD_ADDRESS})" + else + # If the variable does not a value (the current default behavior), so do not obfuscate anything + if [[ -z ${OBFUSCATE} ]]; then + log_write " ${line}" + # Othwerise, a flag was passed to this command to obfuscate domains in the log + else + # So first check if there are domains in the log that should be obfuscated + if [[ -n ${line_to_obfuscate} ]]; then + # 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 " ${line}" + fi + fi + fi + done } -tricorder_use_nc_or_curl() { - # Users can submit their debug logs using nc (unencrypted) or curl (encrypted) if available - # Check for curl first since encryption is a good thing - if command -v curl &> /dev/null; then - # If the command exists, - log_write " * Using ${COL_GREEN}curl${COL_NC} for transmission." - # 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}) - # 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}) +analyze_pihole_log() { + echo_current_diagnostic "Pi-hole log" + local pihole_log_head=() + local pihole_log_tail=() + local pihole_log_permissions + local logging_enabled + + logging_enabled=$(grep -c "^log-queries" /etc/dnsmasq.d/01-pihole.conf) + if [[ "${logging_enabled}" == "0" ]]; then + # Inform user that logging has been disabled and pihole.log does not contain queries + log_write "${INFO} Query logging is disabled" + 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 -ld "${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 } @@ -1239,14 +1364,13 @@ upload_to_tricorder() { # 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 " * 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 [[ "${AUTOMATED}" ]]; then # let the user know log_write "${INFO} Debug script running in automated mode" # 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, else echo "" @@ -1255,9 +1379,9 @@ upload_to_tricorder() { read -r -p "[?] Would you like to upload the log? [y/N] " response case ${response} in # 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 - *) log_write " * Log will ${COL_GREEN}NOT${COL_NC} be uploaded to tricorder.";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 fi # Check if tricorder.pi-hole.net is reachable and provide token @@ -1266,15 +1390,15 @@ upload_to_tricorder() { # 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 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 "${COL_PURPLE}***********************************${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 "" - log_write " * Provide the token above to the Pi-hole team for assistance at" - log_write " * ${FORUMS_URL}" - log_write " * Your log will self-destruct on our server after ${COL_RED}48 hours${COL_NC}." + log_write " * Provide the token above to the Pi-hole team for assistance at ${FORUMS_URL}" + # If no token was generated else # Show an error and some help instructions @@ -1295,10 +1419,13 @@ check_component_versions check_critical_program_versions diagnose_operating_system check_selinux +check_firewalld processor_check check_networking check_name_resolution +check_dhcp_servers process_status +ftl_full_status parse_setup_vars check_x_headers analyze_gravity_list @@ -1307,6 +1434,7 @@ show_domainlist show_clients show_adlists show_content_of_pihole_files +show_messages parse_locale analyze_pihole_log copy_to_debug_log diff --git a/advanced/Scripts/piholeLogFlush.sh b/advanced/Scripts/piholeLogFlush.sh index 51e94d7c..5c6a2c68 100755 --- a/advanced/Scripts/piholeLogFlush.sh +++ b/advanced/Scripts/piholeLogFlush.sh @@ -11,6 +11,11 @@ colfile="/opt/pihole/COL_TABLE" 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 # Obtain DBFILE=... setting from pihole-FTL.db # Constructed to return nothing when @@ -32,7 +37,7 @@ if [[ "$@" == *"once"* ]]; then # Nightly logrotation if command -v /usr/sbin/logrotate >/dev/null; then # Logrotate once - /usr/sbin/logrotate --force /etc/pihole/logrotate + /usr/sbin/logrotate --force --state "${STATEFILE}" /etc/pihole/logrotate else # Copy pihole.log over to pihole.log.1 # and empty out pihole.log @@ -47,8 +52,8 @@ else # Manual flushing if command -v /usr/sbin/logrotate >/dev/null; then # Logrotate twice to move all data out of sight of FTL - /usr/sbin/logrotate --force /etc/pihole/logrotate; sleep 3 - /usr/sbin/logrotate --force /etc/pihole/logrotate + /usr/sbin/logrotate --force --state "${STATEFILE}" /etc/pihole/logrotate; sleep 3 + /usr/sbin/logrotate --force --state "${STATEFILE}" /etc/pihole/logrotate else # Flush both pihole.log and pihole.log.1 (if existing) echo " " > /var/log/pihole.log diff --git a/advanced/Scripts/query.sh b/advanced/Scripts/query.sh index 7518e6c4..26b4508e 100755 --- a/advanced/Scripts/query.sh +++ b/advanced/Scripts/query.sh @@ -1,5 +1,6 @@ #!/usr/bin/env bash # shellcheck disable=SC1090 + # Pi-hole: A black hole for Internet advertisements # (c) 2018 Pi-hole, LLC (https://pi-hole.net) # Network-wide ad blocking via your own hardware. @@ -11,12 +12,21 @@ # Globals piholeDir="/etc/pihole" -gravityDBfile="${piholeDir}/gravity.db" +GRAVITYDB="${piholeDir}/gravity.db" options="$*" all="" exact="" blockpage="" 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" source "${colfile}" @@ -29,7 +39,7 @@ scanList(){ # Prevent grep from printing file path cd "$piholeDir" || exit 1 - # Prevent grep -i matching slowly: http://bit.ly/2xFXtUX + # Prevent grep -i matching slowly: https://bit.ly/2xFXtUX export LC_CTYPE=C # /dev/null forces filename to be printed when only one list has been generated diff --git a/advanced/Scripts/setupLCD.sh b/advanced/Scripts/setupLCD.sh index e8f14f06..82523643 100755 --- a/advanced/Scripts/setupLCD.sh +++ b/advanced/Scripts/setupLCD.sh @@ -70,5 +70,5 @@ setupcon reboot # Start showing the stats on the screen by running the command on another tty: -# http://unix.stackexchange.com/questions/170063/start-a-process-on-a-different-tty +# https://unix.stackexchange.com/questions/170063/start-a-process-on-a-different-tty #setsid sh -c 'exec /usr/local/bin/chronometer.sh <> /dev/tty1 >&0 2>&1' diff --git a/advanced/Scripts/update.sh b/advanced/Scripts/update.sh index f833fc2f..dae04861 100755 --- a/advanced/Scripts/update.sh +++ b/advanced/Scripts/update.sh @@ -95,6 +95,10 @@ main() { # shellcheck disable=1090,2154 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 if ! is_repo "${PI_HOLE_FILES_DIR}" ; then echo -e "\\n ${COL_LIGHT_RED}Error: Core Pi-hole repo is missing from system!" diff --git a/advanced/Scripts/version.sh b/advanced/Scripts/version.sh index f77ee635..86ac45bc 100755 --- a/advanced/Scripts/version.sh +++ b/advanced/Scripts/version.sh @@ -153,7 +153,7 @@ versionOutput() { if [[ -n "$current" ]] && [[ -n "$latest" ]]; then output="${1^} version is $branch$current (Latest: $latest)" 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 output="Latest ${1^} version is $latest" elif [[ "$curHash" == "N/A" ]] || [[ "$latHash" == "N/A" ]]; then diff --git a/advanced/Scripts/webpage.sh b/advanced/Scripts/webpage.sh index 6a8fa64d..52c388f8 100755 --- a/advanced/Scripts/webpage.sh +++ b/advanced/Scripts/webpage.sh @@ -10,18 +10,22 @@ # This file is copyright under the latest version of the EUPL. # Please see LICENSE file for your rights under this license. -readonly setupVars="/etc/pihole/setupVars.conf" readonly dnsmasqconfig="/etc/dnsmasq.d/01-pihole.conf" readonly dhcpconfig="/etc/dnsmasq.d/02-pihole-dhcp.conf" readonly FTLconf="/etc/pihole/pihole-FTL.conf" # 03 -> wildcards readonly dhcpstaticconfig="/etc/dnsmasq.d/04-pihole-static-dhcp.conf" -readonly PI_HOLE_BIN_DIR="/usr/local/bin" readonly dnscustomfile="/etc/pihole/custom.list" readonly dnscustomcnamefile="/etc/dnsmasq.d/05-pihole-custom-cname.conf" readonly gravityDBfile="/etc/pihole/gravity.db" +# Source install script for ${setupVars}, ${PI_HOLE_BIN_DIR} and valid_ip() +readonly PI_HOLE_FILES_DIR="/etc/.pihole" +# shellcheck disable=SC2034 # used in basic-install +PH_TEST="true" +source "${PI_HOLE_FILES_DIR}/automated install/basic-install.sh" + coltable="/opt/pihole/COL_TABLE" if [[ -f ${coltable} ]]; then source ${coltable} @@ -40,7 +44,8 @@ Options: -e, email Set an administrative contact address for the Block Page -h, --help Show this help dialog -i, interface Specify dnsmasq's interface listening behavior - -l, privacylevel Set privacy level (0 = lowest, 4 = highest)" + -l, privacylevel Set privacy level (0 = lowest, 3 = highest) + -t, teleporter Backup configuration as an archive" exit 0 } @@ -49,7 +54,7 @@ add_setting() { } delete_setting() { - sed -i "/${1}/d" "${setupVars}" + sed -i "/^${1}/d" "${setupVars}" } change_setting() { @@ -62,7 +67,7 @@ addFTLsetting() { } deleteFTLsetting() { - sed -i "/${1}/d" "${FTLconf}" + sed -i "/^${1}/d" "${FTLconf}" } changeFTLsetting() { @@ -79,7 +84,7 @@ add_dnsmasq_setting() { } delete_dnsmasq_setting() { - sed -i "/${1}/d" "${dnsmasqconfig}" + sed -i "/^${1}/d" "${dnsmasqconfig}" } SetTemperatureUnit() { @@ -163,9 +168,11 @@ ProcessDNSSettings() { fi delete_dnsmasq_setting "domain-needed" + delete_dnsmasq_setting "expand-hosts" if [[ "${DNS_FQDN_REQUIRED}" == true ]]; then add_dnsmasq_setting "domain-needed" + add_dnsmasq_setting "expand-hosts" fi delete_dnsmasq_setting "bogus-priv" @@ -210,14 +217,79 @@ trust-anchor=.,20326,8,2,E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC68345710423 fi if [[ "${CONDITIONAL_FORWARDING}" == true ]]; then - add_dnsmasq_setting "server=/${CONDITIONAL_FORWARDING_DOMAIN}/${CONDITIONAL_FORWARDING_IP}" - add_dnsmasq_setting "server=/${CONDITIONAL_FORWARDING_REVERSE}/${CONDITIONAL_FORWARDING_IP}" + # Convert legacy "conditional forwarding" to rev-server configuration + # Remove any existing REV_SERVER settings + delete_setting "REV_SERVER" + delete_setting "REV_SERVER_DOMAIN" + delete_setting "REV_SERVER_TARGET" + delete_setting "REV_SERVER_CIDR" + + REV_SERVER=true + add_setting "REV_SERVER" "true" + + REV_SERVER_DOMAIN="${CONDITIONAL_FORWARDING_DOMAIN}" + add_setting "REV_SERVER_DOMAIN" "${REV_SERVER_DOMAIN}" + + REV_SERVER_TARGET="${CONDITIONAL_FORWARDING_IP}" + add_setting "REV_SERVER_TARGET" "${REV_SERVER_TARGET}" + + #Convert CONDITIONAL_FORWARDING_REVERSE if necessary e.g: + # 1.1.168.192.in-addr.arpa to 192.168.1.1/32 + # 1.168.192.in-addr.arpa to 192.168.1.0/24 + # 168.192.in-addr.arpa to 192.168.0.0/16 + # 192.in-addr.arpa to 192.0.0.0/8 + if [[ "${CONDITIONAL_FORWARDING_REVERSE}" == *"in-addr.arpa" ]];then + arrRev=("${CONDITIONAL_FORWARDING_REVERSE//./ }") + case ${#arrRev[@]} in + 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";; + 4 ) REV_SERVER_CIDR="${arrRev[1]}.${arrRev[0]}.0.0/16";; + 3 ) REV_SERVER_CIDR="${arrRev[0]}.0.0.0/8";; + esac + else + # Set REV_SERVER_CIDR to whatever value it was set to + REV_SERVER_CIDR="${CONDITIONAL_FORWARDING_REVERSE}" + fi + + # 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 + # Convert existing input to /24 subnet (preserves legacy behavior) + # This sed converts "192.168.1.2" to "192.168.1.0/24" + # shellcheck disable=2001 + REV_SERVER_CIDR="$(sed "s+\\.[0-9]*$+\\.0/24+" <<< "${REV_SERVER_TARGET}")" + fi + add_setting "REV_SERVER_CIDR" "${REV_SERVER_CIDR}" + + # Remove obsolete settings from setupVars.conf + delete_setting "CONDITIONAL_FORWARDING" + delete_setting "CONDITIONAL_FORWARDING_REVERSE" + delete_setting "CONDITIONAL_FORWARDING_DOMAIN" + delete_setting "CONDITIONAL_FORWARDING_IP" fi - # Prevent Firefox from automatically switching over to DNS-over-HTTPS - # This follows https://support.mozilla.org/en-US/kb/configuring-networks-disable-dns-over-https - # (sourced 7th September 2019) - add_dnsmasq_setting "server=/use-application-dns.net/" + delete_dnsmasq_setting "rev-server" + + if [[ "${REV_SERVER}" == true ]]; then + add_dnsmasq_setting "rev-server=${REV_SERVER_CIDR},${REV_SERVER_TARGET}" + 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}" + fi + + if [[ "${DNS_FQDN_REQUIRED}" != true ]]; then + # Forward unqualified names to the CF target only when the "never + # forward non-FQDN" option is unticked + add_dnsmasq_setting "server=//${REV_SERVER_TARGET}" + fi + + fi + + # 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 + # as we don't want to delete all local=/.../ lines so it's much safer to + # simply rewrite the entire corresponding config file (which is what the + # DHCP settings subroutie is doing) + ProcessDHCPSettings } SetDNSServers() { @@ -226,7 +298,16 @@ SetDNSServers() { IFS=',' read -r -a array <<< "${args[2]}" for index in "${!array[@]}" do - add_setting "PIHOLE_DNS_$((index+1))" "${array[index]}" + # Replace possible "\#" by "#". This fixes AdminLTE#1427 + local ip + ip="${array[index]//\\#/#}" + + if valid_ip "${ip}" || valid_ip6 "${ip}" ; then + add_setting "PIHOLE_DNS_$((index+1))" "${ip}" + else + echo -e " ${CROSS} Invalid IP has been passed" + exit 1 + fi done if [[ "${args[3]}" == "domain-needed" ]]; then @@ -247,16 +328,13 @@ SetDNSServers() { change_setting "DNSSEC" "false" fi - if [[ "${args[6]}" == "conditional_forwarding" ]]; then - change_setting "CONDITIONAL_FORWARDING" "true" - change_setting "CONDITIONAL_FORWARDING_IP" "${args[7]}" - change_setting "CONDITIONAL_FORWARDING_DOMAIN" "${args[8]}" - change_setting "CONDITIONAL_FORWARDING_REVERSE" "${args[9]}" + if [[ "${args[6]}" == "rev-server" ]]; then + change_setting "REV_SERVER" "true" + change_setting "REV_SERVER_CIDR" "${args[7]}" + change_setting "REV_SERVER_TARGET" "${args[8]}" + change_setting "REV_SERVER_DOMAIN" "${args[9]}" else - change_setting "CONDITIONAL_FORWARDING" "false" - delete_setting "CONDITIONAL_FORWARDING_IP" - delete_setting "CONDITIONAL_FORWARDING_DOMAIN" - delete_setting "CONDITIONAL_FORWARDING_REVERSE" + change_setting "REV_SERVER" "false" fi ProcessDNSSettings @@ -334,6 +412,14 @@ dhcp-leasefile=/etc/pihole/dhcp.leases if [[ "${PIHOLE_DOMAIN}" != "none" ]]; then echo "domain=${PIHOLE_DOMAIN}" >> "${dhcpconfig}" + + # When there is a Pi-hole domain set and "Never forward non-FQDNs" is + # ticked, we add `local=/domain/` to tell FTL that this domain is purely + # local and FTL may answer queries from /etc/hosts or DHCP but should + # never forward queries on that domain to any upstream servers + if [[ "${DNS_FQDN_REQUIRED}" == true ]]; then + echo "local=/${PIHOLE_DOMAIN}/" >> "${dhcpconfig}" + fi fi # Sourced from setupVars @@ -346,7 +432,7 @@ dhcp-leasefile=/etc/pihole/dhcp.leases echo "#quiet-dhcp6 #enable-ra 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 " >> "${dhcpconfig}" fi @@ -406,10 +492,15 @@ SetWebUITheme() { } CheckUrl(){ - local regex + local regex check_url # Check for characters NOT allowed in URLs - regex="[^a-zA-Z0-9:/?&%=~._-]" - if [[ "${1}" =~ ${regex} ]]; then + regex="[^a-zA-Z0-9:/?&%=~._()-;]" + + # this will remove first @ that is after schema and before domain + # \1 is optional schema, \2 is userinfo + check_url="$( sed -re 's#([^:/]*://)?([^/]+)@#\1\2#' <<< "$1" )" + + if [[ "${check_url}" =~ ${regex} ]]; then return 1 else return 0 @@ -479,7 +570,13 @@ AddDHCPStaticAddress() { RemoveDHCPStaticAddress() { 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() { @@ -551,8 +648,11 @@ Interfaces: Teleporter() { local datetimestamp + local host 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() @@ -597,8 +697,8 @@ clearAudit() } SetPrivacyLevel() { - # Set privacy level. Minimum is 0, maximum is 4 - if [ "${args[2]}" -ge 0 ] && [ "${args[2]}" -le 4 ]; then + # Set privacy level. Minimum is 0, maximum is 3 + if [ "${args[2]}" -ge 0 ] && [ "${args[2]}" -le 3 ]; then changeFTLsetting "PRIVACYLEVEL" "${args[2]}" pihole restartdns reload-lists fi @@ -620,7 +720,13 @@ RemoveCustomDNSAddress() { ip="${args[2]}" host="${args[3]}" - sed -i "/${ip} ${host}/d" "${dnscustomfile}" + + if valid_ip "${ip}" || valid_ip6 "${ip}" ; then + sed -i "/^${ip} ${host}$/d" "${dnscustomfile}" + else + echo -e " ${CROSS} Invalid IP has been passed" + exit 1 + fi # Restart dnsmasq to update removed custom DNS entries RestartDNS @@ -631,6 +737,7 @@ AddCustomCNAMERecord() { domain="${args[2]}" target="${args[3]}" + echo "cname=${domain},${target}" >> "${dnscustomcnamefile}" # Restart dnsmasq to load new custom CNAME records @@ -642,7 +749,20 @@ RemoveCustomCNAMERecord() { domain="${args[2]}" target="${args[3]}" - sed -i "/cname=${domain},${target}/d" "${dnscustomcnamefile}" + + validDomain="$(checkDomain "${domain}")" + if [[ -n "${validDomain}" ]]; then + validTarget="$(checkDomain "${target}")" + if [[ -n "${validDomain}" ]]; 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 RestartDNS diff --git a/advanced/Templates/gravity.db.sql b/advanced/Templates/gravity.db.sql index 27b8797f..5d7bafa9 100644 --- a/advanced/Templates/gravity.db.sql +++ b/advanced/Templates/gravity.db.sql @@ -16,11 +16,12 @@ CREATE TABLE domainlist ( id INTEGER PRIMARY KEY AUTOINCREMENT, type INTEGER NOT NULL DEFAULT 0, - domain TEXT UNIQUE NOT NULL, + domain TEXT NOT NULL, enabled BOOLEAN NOT NULL DEFAULT 1, date_added 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, + UNIQUE(domain, type) ); CREATE TABLE adlist @@ -30,7 +31,11 @@ CREATE TABLE adlist enabled BOOLEAN NOT NULL DEFAULT 1, date_added 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, + number INTEGER NOT NULL DEFAULT 0, + invalid_domains INTEGER NOT NULL DEFAULT 0, + status INTEGER NOT NULL DEFAULT 0 ); CREATE TABLE adlist_by_group @@ -52,7 +57,7 @@ CREATE TABLE info value TEXT NOT NULL ); -INSERT INTO "info" VALUES('version','12'); +INSERT INTO "info" VALUES('version','14'); CREATE TABLE domain_audit ( @@ -71,7 +76,7 @@ CREATE TABLE domainlist_by_group CREATE TABLE client ( id INTEGER PRIMARY KEY AUTOINCREMENT, - ip TEXT NOL NULL UNIQUE, + ip TEXT NOT NULL UNIQUE, date_added INTEGER NOT NULL DEFAULT (cast(strftime('%s', 'now') as int)), date_modified INTEGER NOT NULL DEFAULT (cast(strftime('%s', 'now') as int)), comment TEXT @@ -84,9 +89,9 @@ CREATE TABLE client_by_group PRIMARY KEY (client_id, group_id) ); -CREATE TRIGGER tr_adlist_update AFTER UPDATE ON adlist +CREATE TRIGGER tr_adlist_update AFTER UPDATE OF address,enabled,comment ON adlist BEGIN - UPDATE adlist SET date_modified = (cast(strftime('%s', 'now') as int)) WHERE address = NEW.address; + UPDATE adlist SET date_modified = (cast(strftime('%s', 'now') as int)) WHERE id = NEW.id; END; CREATE TRIGGER tr_client_update AFTER UPDATE ON client diff --git a/advanced/Templates/pihole-FTL.service b/advanced/Templates/pihole-FTL.service index ab7f8f2b..55a68b15 100644 --- a/advanced/Templates/pihole-FTL.service +++ b/advanced/Templates/pihole-FTL.service @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh ### BEGIN INIT INFO # Provides: pihole-FTL # Required-Start: $remote_fs $syslog $network @@ -9,22 +9,8 @@ # Description: Enable service provided by pihole-FTL daemon ### END INIT INFO -FTLUSER=pihole -PIDFILE=/run/pihole-FTL.pid - -get_pid() { - # First, try to obtain PID from PIDFILE - if [ -s "${PIDFILE}" ]; then - cat "${PIDFILE}" - return - fi - - # If the PIDFILE is empty or not available, obtain the PID using pidof - pidof "pihole-FTL" | awk '{print $(NF)}' -} - is_running() { - ps "$(get_pid)" > /dev/null 2>&1 + pgrep -xo "pihole-FTL" > /dev/null } @@ -34,27 +20,18 @@ start() { echo "pihole-FTL is already running" else # Touch files to ensure they exist (create if non-existing, preserve if existing) - touch /var/log/pihole-FTL.log /var/log/pihole.log - touch /run/pihole-FTL.pid /run/pihole-FTL.port - 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 + mkdir -pm 0755 /run/pihole + touch /run/pihole-FTL.pid /run/pihole-FTL.port /var/log/pihole-FTL.log /var/log/pihole.log /etc/pihole/dhcp.leases # 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 /etc/pihole /etc/pihole/dhcp.leases 2> /dev/null - chown pihole:pihole /var/log/pihole-FTL.log /var/log/pihole.log - chmod 0644 /var/log/pihole-FTL.log /run/pihole-FTL.pid /run/pihole-FTL.port /var/log/pihole.log + 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 + chmod 0644 /run/pihole-FTL.pid /run/pihole-FTL.port /var/log/pihole-FTL.log /var/log/pihole.log /etc/pihole/dhcp.leases /etc/pihole/macvendor.db # 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 - if setcap CAP_NET_BIND_SERVICE,CAP_NET_RAW,CAP_NET_ADMIN+eip "$(which pihole-FTL)"; then - su -s /bin/sh -c "/usr/bin/pihole-FTL" "$FTLUSER" + 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,CAP_IPC_LOCK,CAP_CHOWN+eip "/usr/bin/pihole-FTL"; then + su -s /bin/sh -c "/usr/bin/pihole-FTL" pihole else echo "Warning: Starting pihole-FTL as root because setting capabilities is not supported on this system" - pihole-FTL + /usr/bin/pihole-FTL fi echo fi @@ -63,20 +40,20 @@ start() { # Stop the service stop() { if is_running; then - kill "$(get_pid)" - for i in {1..5}; do + pkill -xo "pihole-FTL" + for i in 1 2 3 4 5; do if ! is_running; then break fi - echo -n "." + printf "." sleep 1 done echo if is_running; then echo "Not stopped; may still be shutting down or shutdown may have failed, killing now" - kill -9 "$(get_pid)" + pkill -xo -9 "pihole-FTL" exit 1 else echo "Stopped" @@ -84,6 +61,8 @@ stop() { else echo "Not running" fi + # Cleanup + rm -f /run/pihole/FTL.sock /dev/shm/FTL-* echo } @@ -112,7 +91,7 @@ case "$1" in start ;; *) - echo $"Usage: $0 {start|stop|restart|reload|status}" + echo "Usage: $0 {start|stop|restart|reload|status}" exit 1 esac diff --git a/advanced/Templates/pihole.cron b/advanced/Templates/pihole.cron index ba89efdb..37724d2e 100644 --- a/advanced/Templates/pihole.cron +++ b/advanced/Templates/pihole.cron @@ -18,19 +18,19 @@ # early morning. Download any updates from the adlists # Squash output to log, then splat the log to stdout on error to allow for # standard crontab job error handling. -59 1 * * 7 root PATH="$PATH:/usr/local/bin/" pihole updateGravity >/var/log/pihole_updateGravity.log || cat /var/log/pihole_updateGravity.log +59 1 * * 7 root PATH="$PATH:/usr/sbin:/usr/local/bin/" pihole updateGravity >/var/log/pihole_updateGravity.log || cat /var/log/pihole_updateGravity.log # Pi-hole: Flush the log daily at 00:00 # The flush script will use logrotate if available # parameter "once": logrotate only once (default is twice) # parameter "quiet": don't print messages -00 00 * * * root PATH="$PATH:/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 -*/10 * * * * root PATH="$PATH:/usr/local/bin/" pihole updatechecker local +*/10 * * * * root PATH="$PATH:/usr/sbin:/usr/local/bin/" pihole updatechecker local # Pi-hole: Grab remote version every 24 hours -59 17 * * * root PATH="$PATH:/usr/local/bin/" pihole updatechecker remote -@reboot root PATH="$PATH:/usr/local/bin/" pihole updatechecker remote reboot +59 17 * * * root PATH="$PATH:/usr/sbin:/usr/local/bin/" pihole updatechecker remote +@reboot root PATH="$PATH:/usr/sbin:/usr/local/bin/" pihole updatechecker remote reboot diff --git a/advanced/bash-completion/pihole b/advanced/bash-completion/pihole index 88282b02..25208a35 100644 --- a/advanced/bash-completion/pihole +++ b/advanced/bash-completion/pihole @@ -56,11 +56,11 @@ _pihole() { ;; "privacylevel") if ( [[ "$prev2" == "admin" ]] || [[ "$prev2" == "-a" ]] ); then - opts_privacy="0 1 2 3 4" + opts_privacy="0 1 2 3" COMPREPLY=( $(compgen -W "${opts_privacy}" -- ${cur}) ) - else + else return 1 - fi + fi ;; "core"|"admin"|"ftl") if [[ "$prev2" == "checkout" ]]; then diff --git a/advanced/blockingpage.css b/advanced/blockingpage.css index 5fd858fb..0cc7a65c 100644 --- a/advanced/blockingpage.css +++ b/advanced/blockingpage.css @@ -6,45 +6,46 @@ * Please see LICENSE file for your rights under this license. */ /* Text Customisation Options ======> */ -.title:before { content: "Website Blocked"; } -.altBtn:before { content: "Why am I here?"; } -.linkPH:before { content: "About Pi-hole"; } -.linkEmail:before { content: "Contact Admin"; } +.title::before { content: "Website Blocked"; } +.altBtn::before { content: "Why am I here?"; } +.linkPH::before { content: "About Pi-hole"; } +.linkEmail::before { content: "Contact Admin"; } -#bpOutput.add:before { content: "Info"; } -#bpOutput.add:after { content: "The domain is being whitelisted..."; } -#bpOutput.error:before, .unhandled:before { content: "Error"; } -#bpOutput.unhandled:after { content: "An unhandled exception occurred. This may happen when your browser is unable to load jQuery, or when the webserver is denying access to the Pi-hole API."; } -#bpOutput.success:before { content: "Success"; } -#bpOutput.success:after { content: "Website has been whitelisted! You may need to flush your DNS cache"; } +#bpOutput.add::before { content: "Info"; } +#bpOutput.add::after { content: "The domain is being whitelisted..."; } +#bpOutput.error::before, .unhandled::before { content: "Error"; } +#bpOutput.unhandled::after { content: "An unhandled exception occurred. This may happen when your browser is unable to load jQuery, or when the webserver is denying access to the Pi-hole API."; } +#bpOutput.success::before { content: "Success"; } +#bpOutput.success::after { content: "Website has been whitelisted! You may need to flush your DNS cache"; } -.recentwl:before { content: "This site has been whitelisted. Please flush your DNS cache and/or restart your browser."; } -.unknown:before { content: "This website is not found in any of Pi-hole's blacklists. The reason you have arrived here is unknown."; } -.cname:before { content: "This site is an alias for "; } /* cname.com */ -.cname:after { content: ", which may be blocked by Pi-hole."; } +.recentwl::before { content: "This site has been whitelisted. Please flush your DNS cache and/or restart your browser."; } +.unknown::before { content: "This website is not found in any of Pi-hole's blacklists. The reason you have arrived here is unknown."; } +.cname::before { content: "This site is an alias for "; } /* cname.com */ +.cname::after { content: ", which may be blocked by Pi-hole."; } -.blacklist:before { content: "Manually Blacklisted"; } -.wildcard:before { content: "Manually Blacklisted by Wildcard"; } -.noblock:before { content: "Not found on any Blacklist"; } +.blacklist::before { content: "Manually Blacklisted"; } +.wildcard::before { content: "Manually Blacklisted by Wildcard"; } +.noblock::before { content: "Not found on any Blacklist"; } -#bpBlock:before { content: "Access to the following website has been denied:"; } -#bpFlag:before { content: "This is primarily due to being flagged as:"; } +#bpBlock::before { content: "Access to the following website has been denied:"; } +#bpFlag::before { content: "This is primarily due to being flagged as:"; } -#bpHelpTxt:before { content: "If you have an ongoing use for this website, please "; } -#bpHelpTxt a:before, #bpHelpTxt span:before { content: "ask the administrator"; } -#bpHelpTxt:after{ content: " of the Pi-hole on this network to have it whitelisted"; } +#bpHelpTxt::before { content: "If you have an ongoing use for this website, please "; } +#bpHelpTxt a::before, #bpHelpTxt span::before { content: "ask the administrator"; } +#bpHelpTxt::after{ content: " of the Pi-hole on this network to have it whitelisted"; } -#bpBack:before { content: "Back to safety"; } -#bpInfo:before { content: "Technical Info"; } -#bpFoundIn:before { content: "This site is found in "; } -#bpFoundIn span:after { content: " of "; } -#bpFoundIn:after { content: " lists:"; } -#bpWhitelist:before { content: "Whitelist"; } +#bpBack::before { content: "Back to safety"; } +#bpInfo::before { content: "Technical Info"; } +#bpFoundIn::before { content: "This site is found in "; } +#bpFoundIn span::after { content: " of "; } +#bpFoundIn::after { content: " lists:"; } +#bpWhitelist::before { content: "Whitelist"; } -footer span:before { content: "Page generated on "; } +footer span::before { content: "Page generated on "; } /* Hide whitelisting form entirely */ /* #bpWLButtons { display: none; } */ + /* Text Customisation Options <=============================== */ /* http://necolas.github.io/normalize.css ======> */ @@ -98,7 +99,7 @@ html { font-size: 62.5%; } a { color: #3c8dbc; text-decoration: none; } a:hover { color: #72afda; text-decoration: underline; } -b { color: rgb(68,68,68); } +b { color: rgb(68, 68, 68); } p { margin: 0; } label, .buttons a { @@ -111,7 +112,7 @@ label, .buttons a { label, .buttons *:not([disabled]) { cursor: pointer; } /* Touch device dark tap highlight */ -header h1 a, label, .buttons * { -webkit-tap-highlight-color: transparent; } +header h1 a, label, .buttons * { -webkit-tap-highlight-color: transparent; } /* Webkit Focus Glow */ textarea, input, button { outline: none; } @@ -120,14 +121,20 @@ textarea, input, button { outline: none; } font-family: "Source Sans Pro"; font-style: normal; font-weight: 400; - src: local("Source Sans Pro"), local("SourceSansPro-Regular"), url("/admin/style/vendor/SourceSansPro/SourceSansPro-Regular.ttf") format("truetype"); + font-display: swap; + src: local("Source Sans Pro Regular"), local("SourceSansPro-Regular"), + url("/admin/style/vendor/SourceSansPro/source-sans-pro-v13-latin-regular.woff2") format("woff2"), + url("/admin/style/vendor/SourceSansPro/source-sans-pro-v13-latin-regular.woff") format("woff"); } @font-face { font-family: "Source Sans Pro"; font-style: normal; font-weight: 700; - src: local("Source Sans Pro Bold"), local("SourceSansPro-Bold"), url("/admin/style/vendor/SourceSansPro/SourceSansPro-Bold.ttf") format("truetype"); + font-display: swap; + src: local("Source Sans Pro Bold"), local("SourceSansPro-Bold"), + url("/admin/style/vendor/SourceSansPro/source-sans-pro-v13-latin-700.woff2") format("woff2"), + url("/admin/style/vendor/SourceSansPro/source-sans-pro-v13-latin-700.woff") format("woff"); } body { @@ -138,14 +145,24 @@ body { } /* 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 b { color: inherit; } #bpWrapper { margin: 0 auto; max-width: 1250px; - box-shadow: 0 0 8px rgba(0,0,0,0.5); + box-shadow: 0 0 8px rgba(0, 0, 0, 0.5); } header { @@ -164,15 +181,15 @@ header h1, header h1 a, header .spc, header #bpAlt label { } h1 a { - background-color: rgba(0,0,0,0.1); - font-family: "Helvetica Neue", Helvetica, Arial ,sans-serif; + background-color: rgba(0, 0, 0, 0.1); + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 2rem; - font-weight: normal; + font-weight: 400; min-width: 230px; - text-align: center; + text-align: center; } -h1 a:hover, header #bpAlt:hover { background-color: rgba(0,0,0,0.12); color: inherit; text-decoration: none; } +h1 a:hover, header #bpAlt:hover { background-color: rgba(0, 0, 0, 0.12); color: inherit; text-decoration: none; } header .spc { width: 100%; } @@ -180,13 +197,34 @@ header #bpAlt label { background: url("/admin/img/logo.svg") no-repeat center left 15px; background-size: 15px 23px; padding: 0 15px; - text-indent: 30px; + text-indent: 30px; } -[type=checkbox][id$="Toggle"] { display: none; } -[type=checkbox][id$="Toggle"]:checked ~ #bpAbout, -[type=checkbox][id$="Toggle"]:checked ~ #bpMoreInfo { - display: block; } +[type="checkbox"][id$="Toggle"] { display: none; } +[type="checkbox"][id$="Toggle"]:checked ~ #bpAbout, +[type="checkbox"][id$="Toggle"]:checked ~ #bpMoreInfo { + 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 */ #bpAboutToggle:checked { @@ -197,28 +235,28 @@ header #bpAlt label { top: 0; opacity: 0; position: absolute; - width: 100%; + width: 100%; } #bpAbout { background: #3c8dbc; border-bottom-left-radius: 5px; - border: 1px solid #FFF; + border: 1px solid #fff; border-right-width: 0; - box-shadow: -1px 1px 1px rgba(0,0,0,0.12); + box-shadow: -1px 1px 1px rgba(0, 0, 0, 0.12); box-sizing: border-box; display: none; font-size: 1.7rem; top: 50px; position: absolute; right: 0; - width: 280px; + width: 280px; z-index: 1; } .aboutPH { box-sizing: border-box; - color: rgba(255,255,255,0.8); + color: rgba(255, 255, 255, 0.8); display: block; padding: 10px; width: 100%; @@ -237,7 +275,7 @@ header #bpAlt label { .aboutPH p { margin: 10px 0; } .aboutPH small { display: block; font-size: 1.2rem; } -.aboutLink { +.aboutLink { background: #fff; border-top: 1px solid #ddd; display: table; @@ -261,16 +299,16 @@ main { #bpOutput { background: #00c0ef; border-radius: 3px; - border: 1px solid rgba(0,0,0,0.1); + border: 1px solid rgba(0, 0, 0, 0.1); color: #fff; font-size: 1.4rem; margin-bottom: 10px; margin-top: 5px; - padding: 15px; + padding: 15px; } -#bpOutput:before { - background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='7' height='14' viewBox='0 0 7 14'%3E%3Cpath fill='%23fff' d='M6,11a1.371,1.371,0,0,1,1,1v1a1.371,1.371,0,0,1-1,1H1a1.371,1.371,0,0,1-1-1V12a1.371,1.371,0,0,1,1-1H2V8H1A1.371,1.371,0,0,1,0,7V6A1.371,1.371,0,0,1,1,5H4A1.371,1.371,0,0,1,5,6v5H6ZM3.5,0A1.5,1.5,0,1,1,2,1.5,1.5,1.5,0,0,1,3.5,0Z'/%3E%3C/svg%3E") no-repeat center left; +#bpOutput::before { + background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='7' height='14' viewBox='0 0 7 14'%3E%3Cpath fill='%23fff' d='M6 11a1.371 1.371 0 011 1v1a1.371 1.371 0 01-1 1H1a1.371 1.371 0 01-1-1v-1a1.371 1.371 0 011-1h1V8H1a1.371 1.371 0 01-1-1V6a1.371 1.371 0 011-1h3a1.371 1.371 0 011 1v5h1zM3.5 0A1.5 1.5 0 112 1.5 1.5 1.5 0 013.5 0z'/%3E%3C/svg%3E") no-repeat center left; display: block; font-size: 1.8rem; text-indent: 15px; @@ -281,8 +319,8 @@ main { #bpOutput.error { background: #dd4b39; } .blockMsg, .flagMsg { - font: bold 1.8rem Consolas, Courier, monospace; - padding: 5px 10px 10px 10px; + font: 700 1.8rem Consolas, Courier, monospace; + padding: 5px 10px 10px; text-indent: 15px; } @@ -298,7 +336,7 @@ main { -moz-appearance: none; -webkit-appearance: none; border-radius: 3px; - border: 1px solid rgba(0,0,0,0.1); + border: 1px solid rgba(0, 0, 0, 0.1); box-sizing: content-box; display: table-cell; font-size: 1.65rem; @@ -309,46 +347,48 @@ main { text-align: center; vertical-align: top; white-space: nowrap; - width: auto; + width: auto; } .buttons a:hover { text-decoration: none; } /* Button hover dark overlay */ .buttons *:not(input):not([disabled]):hover { - background-image: linear-gradient(to bottom, rgba(0,0,0,0.1), rgba(0,0,0,0.1)); - color: #FFF; + background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.1)); + color: #fff; } /* Button active shadow inset */ .buttons *:not([disabled]):not(input):active { - box-shadow: inset 0 3px 5px rgba(0,0,0,0.125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); } /* Input border color */ .buttons *:not([disabled]):hover, .buttons input:focus { - border-color: rgba(0,0,0,0.25); + border-color: rgba(0, 0, 0, 0.25); } -#bpButtons * { width: 50%; color: #FFF; } -#bpBack { background-color: #00a65a; } -#bpInfo { background-color: #3c8dbc; } +#bpButtons * { width: 50%; color: #fff; } +#bpBack { background-color: #00a65a; } +#bpInfo { background-color: #3c8dbc; } #bpWhitelist { background-color: #dd4b39; } -#blockpage .buttons [type=password][disabled] { color: rgba(0,0,0,1); } -#blockpage .buttons [disabled] { color: rgba(0,0,0,0.55); background-color: #e3e3e3; } -#blockpage .buttons [type=password]:-ms-input-placeholder { color: rgba(51,51,51,0.8); } +#blockpage .buttons [type="password"][disabled] { color: rgba(0, 0, 0, 1); } +#blockpage .buttons [disabled] { color: rgba(0, 0, 0, 0.55); background-color: #e3e3e3; } +#blockpage .buttons [type="password"]:-ms-input-placeholder { color: rgba(51, 51, 51, 0.8); } -input[type=password] { font-size: 1.5rem; } +input[type="password"] { font-size: 1.5rem; } + +@-webkit-keyframes slidein { from { max-height: 0; opacity: 0; } to { max-height: 300px; opacity: 1; } } @keyframes slidein { from { max-height: 0; opacity: 0; } to { max-height: 300px; opacity: 1; } } -#bpMoreToggle:checked ~ #bpMoreInfo { display: block; margin-top: 8px; animation: slidein 0.05s linear; } +#bpMoreToggle:checked ~ #bpMoreInfo { display: block; margin-top: 8px; -webkit-animation: slidein 0.05s linear; animation: slidein 0.05s linear; } #bpMoreInfo { display: none; margin-top: 10px; } #bpQueryOutput { font-size: 1.2rem; line-height: 1.65rem; - margin: 5px 0 0 0; + margin: 5px 0 0; overflow: auto; padding: 0 5px; -webkit-overflow-scrolling: touch; @@ -367,17 +407,49 @@ footer { border-top: 1px solid #d2d6de; color: #444; font: 1.2rem Consolas, Courier, monospace; - padding: 8px; + padding: 8px; } /* Responsive Content */ @media only screen and (max-width: 500px) { - h1 a { font-size: 1.8rem; min-width: 170px; } - footer span:before { content: "Generated "; } - footer span { display: block; } + h1 a { + font-size: 1.8rem; + min-width: 170px; + } + + footer span::before { + content: "Generated "; + } + + footer span { + display: block; + } } @media only screen and (min-width: 1251px) { - #bpWrapper, footer { border-radius: 0 0 5px 5px; } - #bpAbout { border-right-width: 1px; } + #bpWrapper, footer { + 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; + } } diff --git a/advanced/index.php b/advanced/index.php index 4f2a17f7..d0c5fc5d 100644 --- a/advanced/index.php +++ b/advanced/index.php @@ -24,7 +24,7 @@ unset($setupVars); $landPage = "../landing.php"; // Define array for hostnames to be accepted as self address for splash page -$authorizedHosts = []; +$authorizedHosts = [ "localhost" ]; if (!empty($_SERVER["FQDN"])) { // If setenv.add-environment = ("fqdn" => "true") is configured in lighttpd, // append $serverName to $authorizedHosts @@ -41,7 +41,7 @@ $validExtTypes = array("asp", "htm", "html", "php", "rss", "xml", ""); $currentUrlExt = pathinfo($_SERVER["REQUEST_URI"], PATHINFO_EXTENSION); // Set mobile friendly viewport -$viewPort = ''; +$viewPort = ''; // Set response header function setHeader($type = "x") { @@ -55,46 +55,71 @@ if ($serverName === "pi.hole" // Redirect to Web Interface exit(header("Location: /admin")); } elseif (filter_var($serverName, FILTER_VALIDATE_IP) || in_array($serverName, $authorizedHosts)) { - // Set Splash Page output - $splashPage = " - - - $viewPort - - - -
- Pi-hole: Your black hole for Internet advertisements
- Did you mean to go to the admin panel? - + // When directly browsing via IP or authorized hostname + // Render splash/landing page based off presence of $landPage file + // Unset variables so as to not be included in $landPage or $splashPage + unset($svPasswd, $svEmail, $authorizedHosts, $validExtTypes, $currentUrlExt); + // If $landPage file is present + if (is_file(getcwd()."/$landPage")) { + unset($serverName, $viewPort); // unset extra variables not to be included in $landpage + include $landPage; + exit(); + } + // If $landPage file was not present, Set Splash Page output + $splashPage = << + + + + $viewPort + ● $serverName + + + + +
+ Pi-hole logo +

Pi-hole: Your black hole for Internet advertisements

+ Did you mean to go to the admin panel? +
+ - "; - - // Set splash/landing page based off presence of $landPage - $renderPage = is_file(getcwd()."/$landPage") ? include $landPage : "$splashPage"; - - // Unset variables so as to not be included in $landPage - unset($serverName, $svPasswd, $svEmail, $authorizedHosts, $validExtTypes, $currentUrlExt, $viewPort); - - // Render splash/landing page when directly browsing via IP or authorized hostname - exit($renderPage); +EOT; + exit($splashPage); } elseif ($currentUrlExt === "js") { - // Serve Pi-hole Javascript for blocked domains requesting JS + // Serve Pi-hole JavaScript for blocked domains requesting JS exit(setHeader("js").'var x = "Pi-hole: A black hole for Internet advertisements."'); } elseif (strpos($_SERVER["REQUEST_URI"], "?") !== FALSE && isset($_SERVER["HTTP_REFERER"])) { // Serve blank image upon receiving REQUEST_URI w/ query string & HTTP_REFERRER // e.g: An iframe of a blocked domain - exit(setHeader().' - - + exit(setHeader().' + + + + + + + '); } elseif (!in_array($currentUrlExt, $validExtTypes) || substr_count($_SERVER["REQUEST_URI"], "?")) { // Serve SVG upon receiving non $validExtTypes URL extension or query string // e.g: Not an iframe of a blocked domain, such as when browsing to a file/query directly // QoL addition: Allow the SVG to be clicked on in order to quickly show the full Block Page - $blockImg = 'Blocked by Pi-hole'; - exit(setHeader()." - $viewPort + $blockImg = ' + + + + + Blocked by Pi-hole + + + '; + exit(setHeader()." + + + + $viewPort + $blockImg "); } @@ -227,7 +252,7 @@ if (explode("-", $phVersion)[1] != "0") setHeader(); ?> - + - + - + - - + + ● <?=$serverName ?>