Merge branch 'development' into wormhole

changed console to bash
clone to fork

Signed-off-by: James Lagermann <james.lagermann@corelight.com>

Update CONTRIBUTING.md

Signed-off-by: XhmikosR <xhmikosr@gmail.com>
This commit is contained in:
James Lagermann 2020-07-21 15:46:00 -05:00
commit da6b78f4c5
No known key found for this signature in database
GPG key ID: 6C22515FCC04F4F2
45 changed files with 969 additions and 879 deletions

View file

@ -1,4 +1,4 @@
# EditorConfig is awesome: http://EditorConfig.org # EditorConfig is awesome: https://editorconfig.org/
# top-most EditorConfig file # top-most EditorConfig file
root = true root = true

View file

@ -9,11 +9,11 @@
`{Replace this with a number from 1 to 10. 1 being not familiar, and 10 being very familiar}` `{Replace this with a number from 1 to 10. 1 being not familiar, and 10 being very familiar}`
--- ---
**Expected behaviour:** **Expected behavior:**
`{A detailed description of what you expect to see}` `{A detailed description of what you expect to see}`
**Actual behaviour:** **Actual behavior:**
`{A detailed description and/or screenshots of what you do see}` `{A detailed description and/or screenshots of what you do see}`

View file

@ -10,7 +10,7 @@
- [ ] It is compatible with the [EUPL 1.2 license](https://opensource.org/licenses/EUPL-1.1) - [ ] 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)) - [ ] 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://github.com/pi-hole/pi-hole/wiki/How-to-signoff-your-commits.) all commits. Pi-hole enforces the [DCO](https://github.com/pi-hole/pi-hole/wiki/Contributing-to-the-project). 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?:** **What does this PR aim to accomplish?:**

2
.gitignore vendored
View file

@ -15,7 +15,7 @@ __pycache__
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# All idea files, with execptions # All idea files, with exceptions
.idea .idea
!.idea/codeStyles/* !.idea/codeStyles/*
!.idea/codeStyleSettings.xml !.idea/codeStyleSettings.xml

View file

@ -2,5 +2,4 @@ linters:
shellcheck: shellcheck:
shell: bash shell: bash
phpcs: phpcs:
csslint:
flake8: flake8:

View file

@ -3,7 +3,7 @@ services:
- docker - docker
language: python language: python
python: python:
- "2.7" - "3.6"
install: install:
- pip install -r requirements.txt - pip install -r requirements.txt

View file

@ -39,18 +39,18 @@ When requesting or submitting new features, first consider whether it might be u
## Forking and Cloning from GitHub to GitHub ## Forking and Cloning from GitHub to GitHub
1. Fork <https://github.com/pi-hole/pi-hole/> to a repo under a namespace you control, or have permission to use, example: `https://github.com/<your_namespace>/<your_repo_name>/`. You can do this from the github.com website. 1. Fork <https://github.com/pi-hole/pi-hole/> to a repo under a namespace you control, or have permission to use, for example: `https://github.com/<your_namespace>/<your_repo_name>/`. You can do this from the github.com website.
2. Clone `https://github.com/<your_namespace>/<your_repo_name>/` with the tool of you choice. 2. Clone `https://github.com/<your_namespace>/<your_repo_name>/` 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. 3. To keep your fork in sync with our repo, add an upstream remote for pi-hole/pi-hole to your repo.
```console ```bash
git remote add upstream https://github.com/pi-hole/pi-hole.git git remote add upstream https://github.com/pi-hole/pi-hole.git
``` ```
4. Checkout the `development` branch from your clone `https://github.com/<your_namespace>/<your_repo_name>/`. 4. Checkout the `development` branch from your fork `https://github.com/<your_namespace>/<your_repo_name>/`.
5. Create a topic/branch, based on the `development` branch code. *Bonus fun to keep to the theme of Star Trek/black holes/gravity.* 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. 6. Make your changes and commit to your topic branch in your repo.
7. Rebase your commits and squash any insignificant commits. See notes below for an example. 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. 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. 9. Open a Pull Request to merge your topic branch into our repo's `development` branch.
@ -58,9 +58,9 @@ When requesting or submitting new features, first consider whether it might be u
## Forking and Cloning from GitHub to other code hosting sites ## 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, from those sites may be able to mirror a GitHub repo. - 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, example: `https://github.com/<your_namespace>/<your_repo_name>/`. 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/<your_namespace>/<your_repo_name>/`.
2. Create a repo in your code hosting site, for example: `https://gitlab.com/<your_namespace>/<your_repo_name>/` 2. Create a repo in your code hosting site, for example: `https://gitlab.com/<your_namespace>/<your_repo_name>/`
3. Follow the instructions from your code hosting site to create a mirror between `https://github.com/<your_namespace>/<your_repo_name>/` and `https://gitlab.com/<your_namespace>/<your_repo_name>/`. 3. Follow the instructions from your code hosting site to create a mirror between `https://github.com/<your_namespace>/<your_repo_name>/` and `https://gitlab.com/<your_namespace>/<your_repo_name>/`.
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/<your_namespace>/<your_repo_name>/`. 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/<your_namespace>/<your_repo_name>/`.
@ -70,7 +70,7 @@ When requesting or submitting new features, first consider whether it might be u
- To rebase your commits and squash previous commits, you can use: - To rebase your commits and squash previous commits, you can use:
```bash ```bash
git rebase -i your_topic_branch~(# of commits to combine) 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) - For more details visit [gitready.com](http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html)
@ -92,20 +92,20 @@ When requesting or submitting new features, first consider whether it might be u
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.* 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.*
```console ```bash
new commit comments new commit comments
Signed-off-by: yourname <your email address> Signed-off-by: yourname <your email address>
``` ```
4. Save and close the editor for the rebase process to execute. The terminal output should say something like the following: 4. Save and close the editor for the rebase process to execute. The terminal output should say something like the following:
```console ```bash
Successfully rebased and updated refs/heads/mytopic. 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: 5. Once you have a successful rebase, and before you sync your local clone, you have to force push origin to update your repo:
```console ```bash
git push -f origin git push -f origin
``` ```

196
README.md
View file

@ -1,14 +1,22 @@
<!-- markdownlint-configure-file { "MD004": { "style": "consistent" } } -->
<!-- markdownlint-disable MD033 -->
<p align="center"> <p align="center">
<a href="https://pi-hole.net"><img src="https://pi-hole.github.io/graphics/Vortex/Vortex_with_text.png" width="150" height="255" alt="Pi-hole"></a><br/> <a href="https://pi-hole.net/">
<b>Network-wide ad blocking via your own Linux hardware</b><br/> <img src="https://pi-hole.github.io/graphics/Vortex/Vortex_with_Wordmark.svg" width="150" height="260" alt="Pi-hole">
</a>
<br>
<strong>Network-wide ad blocking via your own Linux hardware</strong>
</p> </p>
<!-- markdownlint-enable MD033 -->
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](https://www.youtube.com/watch?v=vKWjx1AQYgs)
- **Resolute**: content is blocked in _non-browser locations_, such as ad-laden mobile apps and smart TVs - **Resolute**: content is blocked in _non-browser locations_, such as ad-laden mobile apps and smart TVs
- **Responsive**: seamlessly speeds up the feel of everyday browsing by caching DNS queries - **Responsive**: seamlessly speeds up the feel of everyday browsing by caching DNS queries
- **Lightweight**: runs smoothly with [minimal hardware and software requirements](https://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 - **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 - **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 - **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 - **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&amp;utm_medium=referral&amp;utm_content=pi-hole/pi-hole&amp;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) Master [![Build Status](https://travis-ci.com/pi-hole/pi-hole.svg?branch=master)](https://travis-ci.com/pi-hole/pi-hole) Development [![Build Status](https://travis-ci.com/pi-hole/pi-hole.svg?branch=development)](https://travis-ci.com/pi-hole/pi-hole)
[![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 ## One-Step Automated Install
Those who want to get started quickly and conveniently may install Pi-hole using the following command: Those who want to get started quickly and conveniently may install Pi-hole using the following command:
#### `curl -sSL https://install.pi-hole.net | bash` ### `curl -sSL https://install.pi-hole.net | bash`
## Alternative Install Methods ## 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 ### Method 1: Clone our repository and run
```
```bash
git clone --depth 1 https://github.com/pi-hole/pi-hole.git Pi-hole git clone --depth 1 https://github.com/pi-hole/pi-hole.git Pi-hole
cd "Pi-hole/automated install/" cd "Pi-hole/automated install/"
sudo bash basic-install.sh sudo bash basic-install.sh
``` ```
### Method 2: Manually download the installer and run ### Method 2: Manually download the installer and run
```
```bash
wget -O basic-install.sh https://install.pi-hole.net wget -O basic-install.sh https://install.pi-hole.net
sudo bash basic-install.sh sudo bash basic-install.sh
``` ```
## 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. 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,161 +64,102 @@ 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 ## Pi-hole is free, but powered by your support
There are many reoccurring costs involved with maintaining free, open source, and privacy-respecting software; expenses which [our volunteer developers](https://github.com/orgs/pi-hole/people) pitch in to cover out-of-pocket. This is just one example of how strongly we feel about our software, as well as the importance of keeping it maintained. There are many reoccurring costs involved with maintaining free, open source, and privacy-respecting software; expenses which [our volunteer developers](https://github.com/orgs/pi-hole/people) pitch in to cover out-of-pocket. This is just one example of how strongly we feel about our software, as well as the importance of keeping it maintained.
Make no mistake: **your support is absolutely vital to help keep us innovating!** Make no mistake: **your support is absolutely vital to help keep us innovating!**
### Donations ### [Donations](https://pi-hole.net/donate)
Sending a donation using our links below is **extremely helpful** in offsetting a portion of our monthly expenses:
- <img src="https://pi-hole.github.io/graphics/Badges/paypal-badge-black.svg" width="24" height="24" alt="PP"/> <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=3J2L3Z4DHW9UY">Donate via PayPal</a><br/> Sending a donation using our Sponsor Button is **extremely helpful** in offsetting a portion of our monthly expenses:
- <img src="https://pi-hole.github.io/graphics/Badges/bitcoin-badge-black.svg" width="24" height="24" alt="BTC"/> [Bitcoin, Bitcoin Cash, Ethereum, Litecoin](https://commerce.coinbase.com/checkout/dd304d04-f324-4a77-931b-0db61c77a41b)
### Alternative support ### 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:
If you'd rather not donate (_which is okay!_), there are other ways you can help support us:
- [Patreon](https://patreon.com/pihole) _Become a patron for rewards_ - [Patreon](https://patreon.com/pihole) _Become a patron for rewards_
- [Digital Ocean](http://www.digitalocean.com/?refcode=344d234950e1) _affiliate link_ - [Digital Ocean](https://www.digitalocean.com/?refcode=344d234950e1) _affiliate link_
- [Stickermule](https://www.stickermule.com/unlock?ref_id=9127301701&utm_medium=link&utm_source=invite) _earn a $10 credit after your first purchase_ - [Stickermule](https://www.stickermule.com/unlock?ref_id=9127301701&utm_medium=link&utm_source=invite) _earn a $10 credit after your first purchase_
- [Pi-hole Swag Store](https://pi-hole.net/shop/) _affiliate link_
- [Amazon](http://www.amazon.com/exec/obidos/redirect-home/pihole09-20) _affiliate link_ - [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_
- Spreading the word about our software, and how you have benefited from it - Spreading the word about our software, and how you have benefited from it
### Contributing via GitHub ### Contributing via GitHub
We welcome _everyone_ to contribute to issue reports, suggest new features, and create pull requests. We welcome _everyone_ to contribute to issue reports, suggest new features, and create pull requests.
If you have something to add - anything from a typo through to a whole new feature, we're happy to check it out! Just make sure to fill out our template when submitting your request; the questions that it asks will help the volunteers quickly understand what you're aiming to achieve. If you have something to add - anything from a typo through to a whole new feature, we're happy to check it out! Just make sure to fill out our template when submitting your request; the questions 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. 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 ## Getting in touch with us
While we are primarily reachable on our <a href="https://discourse.pi-hole.net/">Discourse User Forum</a>, 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.
<ul> While we are primarily reachable on our [Discourse User Forum](https://discourse.pi-hole.net/), we can also be found on a variety of social media outlets. **Please be sure to check the FAQ's** before starting a new discussion, as we do not have the spare time to reply to every request for assistance.
<li><a href="https://discourse.pi-hole.net/c/faqs">Frequently Asked Questions</a></li>
<li><a href="https://github.com/pi-hole/pi-hole/wiki">Pi-hole Wiki</a></li> - [Frequently Asked Questions](https://discourse.pi-hole.net/c/faqs)
<li><a href="https://discourse.pi-hole.net/c/feature-requests?order=votes">Feature Requests</a></li> - [Feature Requests](https://discourse.pi-hole.net/c/feature-requests?order=votes)
<li><a href="https://discourse.pi-hole.net/">Discourse User Forum</a></li> - [Reddit](https://www.reddit.com/r/pihole/)
<li><a href="https://www.reddit.com/r/pihole/">Reddit</a></li> - [Twitter](https://twitter.com/The_Pi_hole)
<li><a href="https://gitter.im/pi-hole/pi-hole">Gitter</a> (Real-time chat)</li>
<li><a href="https://twitter.com/The_Pi_Hole">Twitter</a></li>
<li><a href="https://www.youtube.com/channel/UCT5kq9w0wSjogzJb81C9U0w">YouTube</a></li>
<li><a href="https://www.facebook.com/ThePiHole/">Facebook</a></li>
</ul>
----- -----
## Breakdown of Features ## 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`.
<a href="https://pi-hole.github.io/graphics/Screenshots/blacklist-cli.gif"><img src="https://pi-hole.github.io/graphics/Screenshots/blacklist-cli.gif" alt="Pi-hole Blacklist Demo"/></a> ### 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`.
![Pi-hole Blacklist Demo](https://pi-hole.github.io/graphics/Screenshots/blacklist-cli.gif)
Some notable features include: Some notable features include:
* [Whitelisting, Blacklisting and 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 ### 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! 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!
<img src="https://pi-hole.github.io/graphics/Screenshots/pihole-dashboard.png" alt="Pi-hole Dashboard"/></a> ![Pi-hole Dashboard](https://pi-hole.github.io/graphics/Screenshots/pihole-dashboard.png)
Some notable features include: Some notable features include:
* Mobile friendly interface
* Password protection - Mobile friendly interface
* Detailed graphs and doughnut charts - Password protection
* Top lists of domains and clients - Detailed graphs and doughnut charts
* A filterable and sortable query log - Top lists of domains and clients
* Long Term Statistics to view data over user-defined time ranges - A filterable and sortable query log
* The ability to easily manage and configure Pi-hole features - Long Term Statistics to view data over user-defined time ranges
* ... and all the main features of the Command Line Interface! - 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): 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://<IP_ADDPRESS_OF_YOUR_PI_HOLE>/admin/` 1. `http://pi.hole/admin/` (when using Pi-hole as your DNS server)
2. `http://pi.hole/admin/` (when using Pi-hole as your DNS server) 2. `http://<IP_ADDPRESS_OF_YOUR_PI_HOLE>/admin/`
3. `http://pi.hole/` (when using Pi-hole as your DNS server) 3. `http://pi.hole/` (when using Pi-hole as your DNS server)
## Faster-than-light Engine ## 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*! 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: 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). - 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 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)

View file

@ -1,7 +1,7 @@
# Determine if terminal is capable of showing colours # Determine if terminal is capable of showing colors
if [[ -t 1 ]] && [[ $(tput colors) -ge 8 ]]; then if [[ -t 1 ]] && [[ $(tput colors) -ge 8 ]]; then
# Bold and underline may not show up on all clients # Bold and underline may not show up on all clients
# If something MUST be emphasised, use both # If something MUST be emphasized, use both
COL_BOLD='' COL_BOLD=''
COL_ULINE='' COL_ULINE=''

View file

@ -13,7 +13,7 @@ LC_NUMERIC=C
# Retrieve stats from FTL engine # Retrieve stats from FTL engine
pihole-FTL() { pihole-FTL() {
ftl_port=$(cat /var/run/pihole-FTL.port 2> /dev/null) ftl_port=$(cat /run/pihole-FTL.port 2> /dev/null)
if [[ -n "$ftl_port" ]]; then if [[ -n "$ftl_port" ]]; then
# Open connection to FTL # Open connection to FTL
exec 3<>"/dev/tcp/127.0.0.1/$ftl_port" exec 3<>"/dev/tcp/127.0.0.1/$ftl_port"
@ -72,7 +72,7 @@ printFunc() {
# Remove excess characters from main text # Remove excess characters from main text
if [[ "$text_main_len" -gt "$text_main_max_len" ]]; then if [[ "$text_main_len" -gt "$text_main_max_len" ]]; then
# Trim text without colours # Trim text without colors
text_main_trim="${text_main_nocol:0:$text_main_max_len}" text_main_trim="${text_main_nocol:0:$text_main_max_len}"
# Replace with trimmed text # Replace with trimmed text
text_main="${text_main/$text_main_nocol/$text_main_trim}" text_main="${text_main/$text_main_nocol/$text_main_trim}"
@ -88,7 +88,7 @@ printFunc() {
[[ "$spc_num" -le 0 ]] && spc_num="0" [[ "$spc_num" -le 0 ]] && spc_num="0"
spc=$(printf "%${spc_num}s") spc=$(printf "%${spc_num}s")
#spc="${spc// /.}" # Debug: Visualise spaces #spc="${spc// /.}" # Debug: Visualize spaces
printf "%s%s$spc" "$title" "$text_main" printf "%s%s$spc" "$title" "$text_main"
@ -131,7 +131,7 @@ get_init_stats() {
printf "%s%02d:%02d:%02d\\n" "$days" "$hrs" "$mins" "$secs" printf "%s%02d:%02d:%02d\\n" "$days" "$hrs" "$mins" "$secs"
} }
# Set Colour Codes # Set Color Codes
coltable="/opt/pihole/COL_TABLE" coltable="/opt/pihole/COL_TABLE"
if [[ -f "${coltable}" ]]; then if [[ -f "${coltable}" ]]; then
source ${coltable} source ${coltable}
@ -153,7 +153,7 @@ get_init_stats() {
sys_throttle_raw=$(vgt=$(sudo vcgencmd get_throttled); echo "${vgt##*x}") 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 if [[ "$sys_throttle_raw" != "0" ]]; then
case "$sys_throttle_raw" in case "$sys_throttle_raw" in
*0001) thr_type="${COL_YELLOW}Under Voltage";; *0001) thr_type="${COL_YELLOW}Under Voltage";;
@ -269,7 +269,7 @@ get_sys_stats() {
scr_lines="${scr_size[0]}" scr_lines="${scr_size[0]}"
scr_cols="${scr_size[1]}" scr_cols="${scr_size[1]}"
# Determine Chronometer size behaviour # Determine Chronometer size behavior
if [[ "$scr_cols" -ge 58 ]]; then if [[ "$scr_cols" -ge 58 ]]; then
chrono_width="large" chrono_width="large"
elif [[ "$scr_cols" -gt 40 ]]; then elif [[ "$scr_cols" -gt 40 ]]; then
@ -308,7 +308,7 @@ get_sys_stats() {
[[ "${cpu_freq}" == *".0"* ]] && cpu_freq="${cpu_freq/.0/}" [[ "${cpu_freq}" == *".0"* ]] && cpu_freq="${cpu_freq/.0/}"
fi fi
# Determine colour for temperature # Determine color for temperature
if [[ -n "$temp_file" ]]; then if [[ -n "$temp_file" ]]; then
if [[ "$temp_unit" == "C" ]]; then if [[ "$temp_unit" == "C" ]]; then
cpu_temp=$(printf "%.0fc\\n" "$(calcFunc "$(< $temp_file) / 1000")") cpu_temp=$(printf "%.0fc\\n" "$(calcFunc "$(< $temp_file) / 1000")")

View file

@ -104,4 +104,10 @@ upgrade_gravityDB(){
sqlite3 "${database}" < "${scriptPath}/10_to_11.sql" sqlite3 "${database}" < "${scriptPath}/10_to_11.sql"
version=11 version=11
fi fi
if [[ "$version" == "11" ]]; then
# Rename group 0 from "Unassociated" to "Default"
echo -e " ${INFO} Upgrading gravity database from version 11 to 12"
sqlite3 "${database}" < "${scriptPath}/11_to_12.sql"
version=12
fi
} }

View file

@ -0,0 +1,19 @@
.timeout 30000
PRAGMA FOREIGN_KEYS=OFF;
BEGIN TRANSACTION;
UPDATE "group" SET name = 'Default' WHERE id = 0;
UPDATE "group" SET description = 'The default group' WHERE id = 0;
DROP TRIGGER IF EXISTS tr_group_zero;
CREATE TRIGGER tr_group_zero AFTER DELETE ON "group"
BEGIN
INSERT OR IGNORE INTO "group" (id,enabled,name,description) VALUES (0,1,'Default','The default group');
END;
UPDATE info SET value = 12 WHERE property = 'version';
COMMIT;

View file

@ -22,6 +22,9 @@ web=false
domList=() domList=()
typeId="" typeId=""
comment=""
declare -i domaincount
domaincount=0
colfile="/opt/pihole/COL_TABLE" colfile="/opt/pihole/COL_TABLE"
source ${colfile} source ${colfile}
@ -97,10 +100,12 @@ ValidateDomain() {
fi fi
if [[ -n "${validDomain}" ]]; then if [[ -n "${validDomain}" ]]; then
domList=("${domList[@]}" ${validDomain}) domList=("${domList[@]}" "${validDomain}")
else else
echo -e " ${CROSS} ${domain} is not a valid argument or domain name!" echo -e " ${CROSS} ${domain} is not a valid argument or domain name!"
fi fi
domaincount=$((domaincount+1))
} }
ProcessDomainList() { ProcessDomainList() {
@ -151,7 +156,12 @@ AddDomain() {
reload=true reload=true
# Insert only the domain here. The enabled and date_added fields will be filled # Insert only the domain here. The enabled and date_added fields will be filled
# with their default values (enabled = true, date_added = current timestamp) # with their default values (enabled = true, date_added = current timestamp)
if [[ -z "${comment}" ]]; then
sqlite3 "${gravityDBfile}" "INSERT INTO domainlist (domain,type) VALUES ('${domain}',${typeId});" sqlite3 "${gravityDBfile}" "INSERT INTO domainlist (domain,type) VALUES ('${domain}',${typeId});"
else
# also add comment when variable has been set through the "--comment" option
sqlite3 "${gravityDBfile}" "INSERT INTO domainlist (domain,type,comment) VALUES ('${domain}',${typeId},'${comment}');"
fi
} }
RemoveDomain() { RemoveDomain() {
@ -224,8 +234,16 @@ NukeList() {
sqlite3 "${gravityDBfile}" "DELETE FROM domainlist WHERE type = ${typeId};" sqlite3 "${gravityDBfile}" "DELETE FROM domainlist WHERE type = ${typeId};"
} }
for var in "$@"; do GetComment() {
case "${var}" in comment="$1"
if [[ "${comment}" =~ [^a-zA-Z0-9_\#:/\.,\ -] ]]; then
echo " ${CROSS} Found invalid characters in domain comment!"
exit
fi
}
while (( "$#" )); do
case "${1}" in
"-w" | "whitelist" ) typeId=0;; "-w" | "whitelist" ) typeId=0;;
"-b" | "blacklist" ) typeId=1;; "-b" | "blacklist" ) typeId=1;;
"--white-regex" | "white-regex" ) typeId=2;; "--white-regex" | "white-regex" ) typeId=2;;
@ -239,13 +257,15 @@ for var in "$@"; do
"-l" | "--list" ) Displaylist;; "-l" | "--list" ) Displaylist;;
"--nuke" ) NukeList;; "--nuke" ) NukeList;;
"--web" ) web=true;; "--web" ) web=true;;
* ) ValidateDomain "${var}";; "--comment" ) GetComment "${2}"; shift;;
* ) ValidateDomain "${1}";;
esac esac
shift
done done
shift shift
if [[ $# = 0 ]]; then if [[ ${domaincount} == 0 ]]; then
helpFunc helpFunc
fi fi

View file

@ -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

View file

@ -36,13 +36,6 @@ flushARP(){
echo -ne " ${INFO} Flushing network table ..." echo -ne " ${INFO} Flushing network table ..."
fi fi
# Flush ARP cache to avoid re-adding of dead entries
if ! output=$(ip neigh flush all 2>&1); then
echo -e "${OVER} ${CROSS} Failed to clear ARP cache"
echo " Output: ${output}"
return 1
fi
# Truncate network_addresses table in pihole-FTL.db # Truncate network_addresses table in pihole-FTL.db
# This needs to be done before we can truncate the network table due to # This needs to be done before we can truncate the network table due to
# foreign key contraints # foreign key contraints

View file

@ -3,7 +3,7 @@
# (c) 2017 Pi-hole, LLC (https://pi-hole.net) # (c) 2017 Pi-hole, LLC (https://pi-hole.net)
# Network-wide ad blocking via your own hardware. # Network-wide ad blocking via your own hardware.
# #
# Switch Pi-hole subsystems to a different Github branch. # Switch Pi-hole subsystems to a different GitHub branch.
# #
# This file is copyright under the latest version of the EUPL. # This file is copyright under the latest version of the EUPL.
# Please see LICENSE file for your rights under this license. # Please see LICENSE file for your rights under this license.
@ -36,7 +36,7 @@ warning1() {
return 0 return 0
;; ;;
*) *)
echo -e "\\n ${INFO} Branch change has been cancelled" echo -e "\\n ${INFO} Branch change has been canceled"
return 1 return 1
;; ;;
esac esac
@ -84,7 +84,7 @@ checkout() {
echo -e " ${INFO} Shortcut \"dev\" detected - checking out development / devel branches..." echo -e " ${INFO} Shortcut \"dev\" detected - checking out development / devel branches..."
echo "" echo ""
echo -e " ${INFO} Pi-hole Core" echo -e " ${INFO} Pi-hole Core"
fetch_checkout_pull_branch "${PI_HOLE_FILES_DIR}" "development" || { echo " ${CROSS} Unable to pull Core developement branch"; exit 1; } fetch_checkout_pull_branch "${PI_HOLE_FILES_DIR}" "development" || { echo " ${CROSS} Unable to pull Core development branch"; exit 1; }
if [[ "${INSTALL_WEB_INTERFACE}" == "true" ]]; then if [[ "${INSTALL_WEB_INTERFACE}" == "true" ]]; then
echo "" echo ""
echo -e " ${INFO} Web interface" echo -e " ${INFO} Web interface"

View file

@ -87,7 +87,7 @@ PIHOLE_DHCP_CONFIG_FILE="${DNSMASQ_D_DIRECTORY}/02-pihole-dhcp.conf"
PIHOLE_WILDCARD_CONFIG_FILE="${DNSMASQ_D_DIRECTORY}/03-wildcard.conf" PIHOLE_WILDCARD_CONFIG_FILE="${DNSMASQ_D_DIRECTORY}/03-wildcard.conf"
WEB_SERVER_CONFIG_FILE="${WEB_SERVER_CONFIG_DIRECTORY}/lighttpd.conf" WEB_SERVER_CONFIG_FILE="${WEB_SERVER_CONFIG_DIRECTORY}/lighttpd.conf"
#WEB_SERVER_CUSTOM_CONFIG_FILE="${WEB_SERVER_CONFIG_DIRECTORY}/external.conf" WEB_SERVER_CUSTOM_CONFIG_FILE="${WEB_SERVER_CONFIG_DIRECTORY}/external.conf"
PIHOLE_INSTALL_LOG_FILE="${PIHOLE_DIRECTORY}/install.log" PIHOLE_INSTALL_LOG_FILE="${PIHOLE_DIRECTORY}/install.log"
PIHOLE_RAW_BLOCKLIST_FILES="${PIHOLE_DIRECTORY}/list.*" PIHOLE_RAW_BLOCKLIST_FILES="${PIHOLE_DIRECTORY}/list.*"
@ -138,7 +138,7 @@ PIHOLE_FTL_LOG="$(get_ftl_conf_value "LOGFILE" "${LOG_DIRECTORY}/pihole-FTL.log"
PIHOLE_WEB_SERVER_ACCESS_LOG_FILE="${WEB_SERVER_LOG_DIRECTORY}/access.log" PIHOLE_WEB_SERVER_ACCESS_LOG_FILE="${WEB_SERVER_LOG_DIRECTORY}/access.log"
PIHOLE_WEB_SERVER_ERROR_LOG_FILE="${WEB_SERVER_LOG_DIRECTORY}/error.log" PIHOLE_WEB_SERVER_ERROR_LOG_FILE="${WEB_SERVER_LOG_DIRECTORY}/error.log"
# An array of operating system "pretty names" that we officialy support # An array of operating system "pretty names" that we officially support
# We can loop through the array at any time to see if it matches a value # We can loop through the array at any time to see if it matches a value
#SUPPORTED_OS=("Raspbian" "Ubuntu" "Fedora" "Debian" "CentOS") #SUPPORTED_OS=("Raspbian" "Ubuntu" "Fedora" "Debian" "CentOS")
@ -166,11 +166,13 @@ REQUIRED_FILES=("${PIHOLE_CRON_FILE}"
"${PIHOLE_DHCP_CONFIG_FILE}" "${PIHOLE_DHCP_CONFIG_FILE}"
"${PIHOLE_WILDCARD_CONFIG_FILE}" "${PIHOLE_WILDCARD_CONFIG_FILE}"
"${WEB_SERVER_CONFIG_FILE}" "${WEB_SERVER_CONFIG_FILE}"
"${WEB_SERVER_CUSTOM_CONFIG_FILE}"
"${PIHOLE_INSTALL_LOG_FILE}" "${PIHOLE_INSTALL_LOG_FILE}"
"${PIHOLE_RAW_BLOCKLIST_FILES}" "${PIHOLE_RAW_BLOCKLIST_FILES}"
"${PIHOLE_LOCAL_HOSTS_FILE}" "${PIHOLE_LOCAL_HOSTS_FILE}"
"${PIHOLE_LOGROTATE_FILE}" "${PIHOLE_LOGROTATE_FILE}"
"${PIHOLE_SETUP_VARS_FILE}" "${PIHOLE_SETUP_VARS_FILE}"
"${PIHOLE_FTL_CONF_FILE}"
"${PIHOLE_COMMAND}" "${PIHOLE_COMMAND}"
"${PIHOLE_COLTABLE_FILE}" "${PIHOLE_COLTABLE_FILE}"
"${FTL_PID}" "${FTL_PID}"
@ -296,11 +298,15 @@ compare_local_version_to_git_version() {
log_write "${INFO} ${pihole_component}: ${COL_YELLOW}${remote_version:-Untagged}${COL_NC} (${FAQ_UPDATE_PI_HOLE})" log_write "${INFO} ${pihole_component}: ${COL_YELLOW}${remote_version:-Untagged}${COL_NC} (${FAQ_UPDATE_PI_HOLE})"
fi fi
# Print the repo upstreams
remotes=$(git remote -v)
log_write "${INFO} Remotes: ${remotes//$'\n'/'\n '}"
# If the repo is on the master branch, 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 if [[ "${remote_branch}" == "master" ]]; then
# so the color of the text is green # so the color of the text is green
log_write "${INFO} Branch: ${COL_GREEN}${remote_branch}${COL_NC}" log_write "${INFO} Branch: ${COL_GREEN}${remote_branch}${COL_NC}"
# If it is any other branch, they are in a developement branch # If it is any other branch, they are in a development branch
else else
# So show that in yellow, signifying it's something to take a look at, but not a critical error # So show that in yellow, signifying it's something to take a look at, but not a critical error
log_write "${INFO} Branch: ${COL_YELLOW}${remote_branch:-Detached}${COL_NC} (${FAQ_CHECKOUT_COMMAND})" log_write "${INFO} Branch: ${COL_YELLOW}${remote_branch:-Detached}${COL_NC} (${FAQ_CHECKOUT_COMMAND})"
@ -357,7 +363,7 @@ check_component_versions() {
get_program_version() { get_program_version() {
local program_name="${1}" local program_name="${1}"
# Create a loval variable so this function can be safely reused # Create a local variable so this function can be safely reused
local program_version local program_version
echo_current_diagnostic "${program_name} version" echo_current_diagnostic "${program_name} version"
# Evalutate the program we are checking, if it is any of the ones below, show the version # Evalutate the program we are checking, if it is any of the ones below, show the version
@ -387,53 +393,53 @@ check_critical_program_versions() {
get_program_version "php" get_program_version "php"
} }
is_os_supported() { os_check() {
local os_to_check="${1}" # This function gets a list of supported OS versions from a TXT record at versions.pi-hole.net
# Strip just the base name of the system using sed # and determines whether or not the script is running on one of those systems
# shellcheck disable=SC2001 local remote_os_domain valid_os valid_version detected_os_pretty detected_os detected_version
the_os=$(echo "${os_to_check}" | sed 's/ .*//') remote_os_domain="versions.pi-hole.net"
# If the variable is one of our supported OSes, valid_os=false
case "${the_os}" in valid_version=false
# 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
}
get_distro_attributes() { detected_os_pretty=$(cat /etc/*release | grep PRETTY_NAME | cut -d '=' -f2- | tr -d '"')
# Put the current Internal Field Separator into another variable so it can be restored later detected_os="${detected_os_pretty%% *}"
OLD_IFS="$IFS" detected_version=$(cat /etc/*release | grep VERSION_ID | cut -d '=' -f2- | tr -d '"')
# 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) )'
# Set a named variable for better readability IFS=" " read -r -a supportedOS < <(dig +short -t txt ${remote_os_domain} | tr -d '"')
local distro_attribute
# For each line found in an /etc/*release file, for i in "${supportedOS[@]}"
for distro_attribute in "${distro_info[@]}"; do do
# store the key in a variable os_part=$(echo "$i" | cut -d '=' -f1)
local pretty_name_key versions_part=$(echo "$i" | cut -d '=' -f2-)
pretty_name_key=$(echo "${distro_attribute}" | grep "PRETTY_NAME" | cut -d '=' -f1)
# we need just the OS PRETTY_NAME, if [[ "${detected_os}" =~ ${os_part} ]]; then
if [[ "${pretty_name_key}" == "PRETTY_NAME" ]]; then valid_os=true
# so save in in a variable when we find it IFS="," read -r -a supportedVer <<<"${versions_part}"
PRETTY_NAME_VALUE=$(echo "${distro_attribute}" | grep "PRETTY_NAME" | cut -d '=' -f2- | tr -d '"') for x in "${supportedVer[@]}"
# then pass it as an argument that checks if the OS is supported do
is_os_supported "${PRETTY_NAME_VALUE}" if [[ "${detected_version}" =~ $x ]];then
else valid_version=true
# Since we only need the pretty name, we can just skip over anything that is not a match break
:
fi fi
done done
# Set the IFS back to what it was break
IFS="$OLD_IFS" fi
done
# Display findings back to the user
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() { diagnose_operating_system() {
@ -445,7 +451,7 @@ diagnose_operating_system() {
# If there is a /etc/*release file, it's probably a supported operating system, so we can # 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 if ls /etc/*release 1> /dev/null 2>&1; then
# display the attributes to the user from the function made earlier # display the attributes to the user from the function made earlier
get_distro_attributes os_check
else else
# If it doesn't exist, it's not a system we currently support and link to FAQ # 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})" log_write "${CROSS} ${COL_RED}${error_msg}${COL_NC} (${FAQ_HARDWARE_REQUIREMENTS})"
@ -747,7 +753,7 @@ check_x_headers() {
# Do it for the dashboard as well, as the header is different than above # Do it for the dashboard as well, as the header is different than above
local dashboard local dashboard
dashboard=$(curl -Is localhost/admin/ | awk '/X-Pi-hole/' | tr -d '\r') dashboard=$(curl -Is localhost/admin/ | awk '/X-Pi-hole/' | tr -d '\r')
# Store what the X-Header shoud be in variables for comparision later # Store what the X-Header shoud be in variables for comparison later
local block_page_working local block_page_working
block_page_working="X-Pi-hole: A black hole for Internet advertisements." block_page_working="X-Pi-hole: A black hole for Internet advertisements."
local dashboard_working local dashboard_working
@ -818,7 +824,7 @@ dig_at() {
# First, do a dig on localhost to see if Pi-hole can use itself to block a domain # 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 local_dig=$(dig +tries=1 +time=2 -"${protocol}" "${random_url}" @${local_address} +short "${record_type}"); then
# If it can, show sucess # 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})" log_write "${TICK} ${random_url} ${COL_GREEN}is ${local_dig}${COL_NC} via ${COL_CYAN}localhost$COL_NC (${local_address})"
else else
# Otherwise, show a failure # Otherwise, show a failure
@ -969,7 +975,7 @@ check_name_resolution() {
# This function can check a directory exists # This function can check a directory exists
# Pi-hole has files in several places, so we will reuse this function # Pi-hole has files in several places, so we will reuse this function
dir_check() { dir_check() {
# Set the first argument passed to tihs function as a named variable for better readability # Set the first argument passed to this function as a named variable for better readability
local directory="${1}" local directory="${1}"
# Display the current test that is running # Display the current test that is running
echo_current_diagnostic "contents of ${COL_CYAN}${directory}${COL_NC}" echo_current_diagnostic "contents of ${COL_CYAN}${directory}${COL_NC}"
@ -987,14 +993,14 @@ dir_check() {
} }
list_files_in_dir() { list_files_in_dir() {
# Set the first argument passed to tihs function as a named variable for better readability # Set the first argument passed to this function as a named variable for better readability
local dir_to_parse="${1}" local dir_to_parse="${1}"
# Store the files found in an array # Store the files found in an array
mapfile -t files_found < <(ls "${dir_to_parse}") mapfile -t files_found < <(ls "${dir_to_parse}")
# For each file in the array, # For each file in the array,
for each_file in "${files_found[@]}"; do for each_file in "${files_found[@]}"; do
if [[ -d "${dir_to_parse}/${each_file}" ]]; then if [[ -d "${dir_to_parse}/${each_file}" ]]; then
# If it's a directoy, do nothing # If it's a directory, do nothing
: :
elif [[ "${dir_to_parse}/${each_file}" == "${PIHOLE_DEBUG_LOG}" ]] || \ elif [[ "${dir_to_parse}/${each_file}" == "${PIHOLE_DEBUG_LOG}" ]] || \
[[ "${dir_to_parse}/${each_file}" == "${PIHOLE_RAW_BLOCKLIST_FILES}" ]] || \ [[ "${dir_to_parse}/${each_file}" == "${PIHOLE_RAW_BLOCKLIST_FILES}" ]] || \
@ -1107,22 +1113,19 @@ show_db_entries() {
} }
show_groups() { show_groups() {
show_db_entries "Groups" "SELECT id,name,enabled,datetime(date_added,'unixepoch','localtime') date_added,datetime(date_modified,'unixepoch','localtime') date_modified,description FROM \"group\"" "4 50 7 19 19 50" 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_adlists() {
show_db_entries "Adlists" "SELECT id,address,enabled,datetime(date_added,'unixepoch','localtime') date_added,datetime(date_modified,'unixepoch','localtime') date_modified,comment FROM adlist" "4 100 7 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;" "4 7 12 100 19 19 50"
show_db_entries "Adlist groups" "SELECT * FROM adlist_by_group" "4 4"
} }
show_domainlist() { show_domainlist() {
show_db_entries "Domainlist (0/1 = exact/regex whitelist, 2/3 = exact/regex blacklist)" "SELECT id,type,domain,enabled,datetime(date_added,'unixepoch','localtime') date_added,datetime(date_modified,'unixepoch','localtime') date_modified,comment FROM domainlist" "4 4 100 7 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;" "4 4 7 12 100 19 19 50"
show_db_entries "Domainlist groups" "SELECT * FROM domainlist_by_group" "10 10"
} }
show_clients() { show_clients() {
show_db_entries "Clients" "SELECT id,ip,datetime(date_added,'unixepoch','localtime') date_added,datetime(date_modified,'unixepoch','localtime') date_modified,comment FROM client" "4 100 19 19 50" 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_db_entries "Client groups" "SELECT * FROM client_by_group" "10 10"
} }
analyze_gravity_list() { analyze_gravity_list() {
@ -1190,7 +1193,7 @@ analyze_pihole_log() {
# So first check if there are domains in the log that should be obfuscated # So first check if there are domains in the log that should be obfuscated
if [[ -n ${line_to_obfuscate} ]]; then 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) # If there are, we need to use awk to replace only the domain name (the 6th field in the log)
# so we substitue the domain for the placeholder value # 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}') obfuscated_line=$(echo "${line_to_obfuscate}" | awk -v placeholder="${OBFUSCATED_PLACEHOLDER}" '{sub($6,placeholder); print $0}')
log_write " ${obfuscated_line}" log_write " ${obfuscated_line}"
else else
@ -1212,6 +1215,11 @@ tricorder_use_nc_or_curl() {
log_write " * Using ${COL_GREEN}curl${COL_NC} for transmission." log_write " * Using ${COL_GREEN}curl${COL_NC} for transmission."
# transmit he log via TLS and store the token returned in a variable # 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}) tricorder_token=$(curl --silent --upload-file ${PIHOLE_DEBUG_LOG} https://tricorder.pi-hole.net:${TRICORDER_SSL_PORT_NUMBER})
if [ -z "${tricorder_token}" ]; then
# curl failed, fallback to nc
log_write " * ${COL_GREEN}curl${COL_NC} failed, falling back to ${COL_YELLOW}netcat${COL_NC} for transmission."
tricorder_token=$(< ${PIHOLE_DEBUG_LOG} nc tricorder.pi-hole.net ${TRICORDER_NC_PORT_NUMBER})
fi
# Otherwise, # Otherwise,
else else
# use net cat # use net cat
@ -1238,7 +1246,7 @@ upload_to_tricorder() {
log_write " * The debug log can be uploaded to tricorder.pi-hole.net for sharing with developers only." log_write " * The debug log can be uploaded to tricorder.pi-hole.net for sharing with developers only."
log_write " * For more information, see: ${TRICORDER_CONTEST}" log_write " * 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." 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 throught the dashboard) # If pihole -d is running automatically (usually through the dashboard)
if [[ "${AUTOMATED}" ]]; then if [[ "${AUTOMATED}" ]]; then
# let the user know # let the user know
log_write "${INFO} Debug script running in automated mode" log_write "${INFO} Debug script running in automated mode"
@ -1254,7 +1262,7 @@ upload_to_tricorder() {
# If they say yes, run our function for uploading the log # If they say yes, run our function for uploading the log
[yY][eE][sS]|[yY]) tricorder_use_nc_or_curl;; [yY][eE][sS]|[yY]) tricorder_use_nc_or_curl;;
# If they choose no, just exit out of the script # If they choose no, just exit out of the script
*) log_write " * Log will ${COL_GREEN}NOT${COL_NC} be uploaded to tricorder.";exit; *) log_write " * Log will ${COL_GREEN}NOT${COL_NC} be uploaded to tricorder.\\n * A local copy of the debug log can be found at: ${COL_CYAN}${PIHOLE_DEBUG_LOG}${COL_NC}\\n";exit;
esac esac
fi fi
# Check if tricorder.pi-hole.net is reachable and provide token # Check if tricorder.pi-hole.net is reachable and provide token

View file

@ -29,19 +29,21 @@ scanList(){
# Prevent grep from printing file path # Prevent grep from printing file path
cd "$piholeDir" || exit 1 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 export LC_CTYPE=C
# /dev/null forces filename to be printed when only one list has been generated # /dev/null forces filename to be printed when only one list has been generated
# shellcheck disable=SC2086
case "${type}" in case "${type}" in
"exact" ) grep -i -E -l "(^|(?<!#)\\s)${esc_domain}($|\\s|#)" ${lists} /dev/null 2>/dev/null;; "exact" ) grep -i -E -l "(^|(?<!#)\\s)${esc_domain}($|\\s|#)" ${lists} /dev/null 2>/dev/null;;
# Create array of regexps
# Iterate through each regexp and check whether it matches the domainQuery # Iterate through each regexp and check whether it matches the domainQuery
# If it does, print the matching regexp and continue looping # If it does, print the matching regexp and continue looping
# Input 1 - regexps | Input 2 - domainQuery # Input 1 - regexps | Input 2 - domainQuery
"regex" ) awk 'NR==FNR{regexps[$0];next}{for (r in regexps)if($0 ~ r)print r}' \ "regex" )
<(echo "${lists}") <(echo "${domain}") 2>/dev/null;; for list in ${lists}; do
if [[ "${domain}" =~ ${list} ]]; then
printf "%b\n" "${list}";
fi
done;;
* ) grep -i "${esc_domain}" ${lists} /dev/null 2>/dev/null;; * ) grep -i "${esc_domain}" ${lists} /dev/null 2>/dev/null;;
esac esac
} }

View file

@ -20,7 +20,7 @@ getInitSys() {
elif [ -f /etc/init.d/cron ] && [ ! -h /etc/init.d/cron ]; then elif [ -f /etc/init.d/cron ] && [ ! -h /etc/init.d/cron ]; then
SYSTEMD=0 SYSTEMD=0
else else
echo "Unrecognised init system" echo "Unrecognized init system"
return 1 return 1
fi fi
} }
@ -70,5 +70,5 @@ setupcon
reboot reboot
# Start showing the stats on the screen by running the command on another tty: # 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' #setsid sh -c 'exec /usr/local/bin/chronometer.sh <> /dev/tty1 >&0 2>&1'

View file

@ -84,6 +84,21 @@ getRemoteVersion(){
# Get the version from the remote origin # Get the version from the remote origin
local daemon="${1}" local daemon="${1}"
local version local version
local cachedVersions
local arrCache
cachedVersions="/etc/pihole/GitHubVersions"
#If the above file exists, then we can read from that. Prevents overuse of GitHub API
if [[ -f "$cachedVersions" ]]; then
IFS=' ' read -r -a arrCache < "$cachedVersions"
case $daemon in
"pi-hole" ) echo "${arrCache[0]}";;
"AdminLTE" ) echo "${arrCache[1]}";;
"FTL" ) echo "${arrCache[2]}";;
esac
return 0
fi
version=$(curl --silent --fail "https://api.github.com/repos/pi-hole/${daemon}/releases/latest" | \ version=$(curl --silent --fail "https://api.github.com/repos/pi-hole/${daemon}/releases/latest" | \
awk -F: '$1 ~/tag_name/ { print $2 }' | \ awk -F: '$1 ~/tag_name/ { print $2 }' | \
@ -97,22 +112,48 @@ getRemoteVersion(){
return 0 return 0
} }
getLocalBranch(){
# Get the checked out branch of the local directory
local directory="${1}"
local branch
# Local FTL btranch is stored in /etc/pihole/ftlbranch
if [[ "$1" == "FTL" ]]; then
branch="$(pihole-FTL branch)"
else
cd "${directory}" 2> /dev/null || { echo "${DEFAULT}"; return 1; }
branch=$(git rev-parse --abbrev-ref HEAD || echo "$DEFAULT")
fi
if [[ ! "${branch}" =~ ^v ]]; then
if [[ "${branch}" == "master" ]]; then
echo ""
elif [[ "${branch}" == "HEAD" ]]; then
echo "in detached HEAD state at "
else
echo "${branch} "
fi
else
# Branch started in "v"
echo "release "
fi
return 0
}
versionOutput() { versionOutput() {
[[ "$1" == "pi-hole" ]] && GITDIR=$COREGITDIR [[ "$1" == "pi-hole" ]] && GITDIR=$COREGITDIR
[[ "$1" == "AdminLTE" ]] && GITDIR=$WEBGITDIR [[ "$1" == "AdminLTE" ]] && GITDIR=$WEBGITDIR
[[ "$1" == "FTL" ]] && GITDIR="FTL" [[ "$1" == "FTL" ]] && GITDIR="FTL"
[[ "$2" == "-c" ]] || [[ "$2" == "--current" ]] || [[ -z "$2" ]] && current=$(getLocalVersion $GITDIR) [[ "$2" == "-c" ]] || [[ "$2" == "--current" ]] || [[ -z "$2" ]] && current=$(getLocalVersion $GITDIR) && branch=$(getLocalBranch $GITDIR)
[[ "$2" == "-l" ]] || [[ "$2" == "--latest" ]] || [[ -z "$2" ]] && latest=$(getRemoteVersion "$1") [[ "$2" == "-l" ]] || [[ "$2" == "--latest" ]] || [[ -z "$2" ]] && latest=$(getRemoteVersion "$1")
if [[ "$2" == "-h" ]] || [[ "$2" == "--hash" ]]; then if [[ "$2" == "-h" ]] || [[ "$2" == "--hash" ]]; then
[[ "$3" == "-c" ]] || [[ "$3" == "--current" ]] || [[ -z "$3" ]] && curHash=$(getLocalHash "$GITDIR") [[ "$3" == "-c" ]] || [[ "$3" == "--current" ]] || [[ -z "$3" ]] && curHash=$(getLocalHash "$GITDIR") && branch=$(getLocalBranch $GITDIR)
[[ "$3" == "-l" ]] || [[ "$3" == "--latest" ]] || [[ -z "$3" ]] && latHash=$(getRemoteHash "$1" "$(cd "$GITDIR" 2> /dev/null && git rev-parse --abbrev-ref HEAD)") [[ "$3" == "-l" ]] || [[ "$3" == "--latest" ]] || [[ -z "$3" ]] && latHash=$(getRemoteHash "$1" "$(cd "$GITDIR" 2> /dev/null && git rev-parse --abbrev-ref HEAD)")
fi fi
if [[ -n "$current" ]] && [[ -n "$latest" ]]; then if [[ -n "$current" ]] && [[ -n "$latest" ]]; then
output="${1^} version is $current (Latest: $latest)" output="${1^} version is $branch$current (Latest: $latest)"
elif [[ -n "$current" ]] && [[ -z "$latest" ]]; then elif [[ -n "$current" ]] && [[ -z "$latest" ]]; then
output="Current ${1^} version is $current" output="Current ${1^} version is $branch$current."
elif [[ -z "$current" ]] && [[ -n "$latest" ]]; then elif [[ -z "$current" ]] && [[ -n "$latest" ]]; then
output="Latest ${1^} version is $latest" output="Latest ${1^} version is $latest"
elif [[ "$curHash" == "N/A" ]] || [[ "$latHash" == "N/A" ]]; then elif [[ "$curHash" == "N/A" ]] || [[ "$latHash" == "N/A" ]]; then
@ -162,7 +203,7 @@ Repositories:
Options: Options:
-c, --current Return the current version -c, --current Return the current version
-l, --latest Return the latest version -l, --latest Return the latest version
--hash Return the Github hash from your local repositories --hash Return the GitHub hash from your local repositories
-h, --help Show this help dialog" -h, --help Show this help dialog"
exit 0 exit 0
} }

View file

@ -10,17 +10,22 @@
# This file is copyright under the latest version of the EUPL. # This file is copyright under the latest version of the EUPL.
# Please see LICENSE file for your rights under this license. # 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 dnsmasqconfig="/etc/dnsmasq.d/01-pihole.conf"
readonly dhcpconfig="/etc/dnsmasq.d/02-pihole-dhcp.conf" readonly dhcpconfig="/etc/dnsmasq.d/02-pihole-dhcp.conf"
readonly FTLconf="/etc/pihole/pihole-FTL.conf" readonly FTLconf="/etc/pihole/pihole-FTL.conf"
# 03 -> wildcards # 03 -> wildcards
readonly dhcpstaticconfig="/etc/dnsmasq.d/04-pihole-static-dhcp.conf" 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 dnscustomfile="/etc/pihole/custom.list"
readonly dnscustomcnamefile="/etc/dnsmasq.d/05-pihole-custom-cname.conf"
readonly gravityDBfile="/etc/pihole/gravity.db" 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" coltable="/opt/pihole/COL_TABLE"
if [[ -f ${coltable} ]]; then if [[ -f ${coltable} ]]; then
source ${coltable} source ${coltable}
@ -36,7 +41,6 @@ Options:
-c, celsius Set Celsius as preferred temperature unit -c, celsius Set Celsius as preferred temperature unit
-f, fahrenheit Set Fahrenheit as preferred temperature unit -f, fahrenheit Set Fahrenheit as preferred temperature unit
-k, kelvin Set Kelvin as preferred temperature unit -k, kelvin Set Kelvin as preferred temperature unit
-r, hostrecord Add a name to the DNS associated to an IPv4/IPv6 address
-e, email Set an administrative contact address for the Block Page -e, email Set an administrative contact address for the Block Page
-h, --help Show this help dialog -h, --help Show this help dialog
-i, interface Specify dnsmasq's interface listening behavior -i, interface Specify dnsmasq's interface listening behavior
@ -179,7 +183,6 @@ ProcessDNSSettings() {
if [[ "${DNSSEC}" == true ]]; then if [[ "${DNSSEC}" == true ]]; then
echo "dnssec echo "dnssec
trust-anchor=.,19036,8,2,49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5
trust-anchor=.,20326,8,2,E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D trust-anchor=.,20326,8,2,E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D
" >> "${dnsmasqconfig}" " >> "${dnsmasqconfig}"
fi fi
@ -211,8 +214,34 @@ trust-anchor=.,20326,8,2,E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC68345710423
fi fi
if [[ "${CONDITIONAL_FORWARDING}" == true ]]; then if [[ "${CONDITIONAL_FORWARDING}" == true ]]; then
add_dnsmasq_setting "server=/${CONDITIONAL_FORWARDING_DOMAIN}/${CONDITIONAL_FORWARDING_IP}" # Convert legacy "conditional forwarding" to rev-server configuration
add_dnsmasq_setting "server=/${CONDITIONAL_FORWARDING_REVERSE}/${CONDITIONAL_FORWARDING_IP}" 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}"
# 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"
# 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}")"
add_setting "REV_SERVER_CIDR" "${REV_SERVER_CIDR}"
fi
if [[ "${REV_SERVER}" == true ]]; then
add_dnsmasq_setting "rev-server=${REV_SERVER_CIDR},${REV_SERVER_TARGET}"
if [ -n "${REV_SERVER_DOMAIN}" ]; then
add_dnsmasq_setting "server=/${REV_SERVER_DOMAIN}/${REV_SERVER_TARGET}"
fi
fi fi
# Prevent Firefox from automatically switching over to DNS-over-HTTPS # Prevent Firefox from automatically switching over to DNS-over-HTTPS
@ -227,7 +256,16 @@ SetDNSServers() {
IFS=',' read -r -a array <<< "${args[2]}" IFS=',' read -r -a array <<< "${args[2]}"
for index in "${!array[@]}" for index in "${!array[@]}"
do 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 done
if [[ "${args[3]}" == "domain-needed" ]]; then if [[ "${args[3]}" == "domain-needed" ]]; then
@ -248,16 +286,13 @@ SetDNSServers() {
change_setting "DNSSEC" "false" change_setting "DNSSEC" "false"
fi fi
if [[ "${args[6]}" == "conditional_forwarding" ]]; then if [[ "${args[6]}" == "rev-server" ]]; then
change_setting "CONDITIONAL_FORWARDING" "true" change_setting "REV_SERVER" "true"
change_setting "CONDITIONAL_FORWARDING_IP" "${args[7]}" change_setting "REV_SERVER_CIDR" "${args[7]}"
change_setting "CONDITIONAL_FORWARDING_DOMAIN" "${args[8]}" change_setting "REV_SERVER_TARGET" "${args[8]}"
change_setting "CONDITIONAL_FORWARDING_REVERSE" "${args[9]}" change_setting "REV_SERVER_DOMAIN" "${args[9]}"
else else
change_setting "CONDITIONAL_FORWARDING" "false" change_setting "REV_SERVER" "false"
delete_setting "CONDITIONAL_FORWARDING_IP"
delete_setting "CONDITIONAL_FORWARDING_DOMAIN"
delete_setting "CONDITIONAL_FORWARDING_REVERSE"
fi fi
ProcessDNSSettings ProcessDNSSettings
@ -402,12 +437,28 @@ SetWebUILayout() {
change_setting "WEBUIBOXEDLAYOUT" "${args[2]}" change_setting "WEBUIBOXEDLAYOUT" "${args[2]}"
} }
SetWebUITheme() {
change_setting "WEBTHEME" "${args[2]}"
}
CheckUrl(){
local regex
# Check for characters NOT allowed in URLs
regex="[^a-zA-Z0-9:/?&%=~._-]"
if [[ "${1}" =~ ${regex} ]]; then
return 1
else
return 0
fi
}
CustomizeAdLists() { CustomizeAdLists() {
local address local address
address="${args[3]}" address="${args[3]}"
local comment local comment
comment="${args[4]}" comment="${args[4]}"
if CheckUrl "${address}"; then
if [[ "${args[2]}" == "enable" ]]; then if [[ "${args[2]}" == "enable" ]]; then
sqlite3 "${gravityDBfile}" "UPDATE adlist SET enabled = 1 WHERE address = '${address}'" sqlite3 "${gravityDBfile}" "UPDATE adlist SET enabled = 1 WHERE address = '${address}'"
elif [[ "${args[2]}" == "disable" ]]; then elif [[ "${args[2]}" == "disable" ]]; then
@ -420,6 +471,10 @@ CustomizeAdLists() {
echo "Not permitted" echo "Not permitted"
return 1 return 1
fi fi
else
echo "Invalid Url"
return 1
fi
} }
SetPrivacyMode() { SetPrivacyMode() {
@ -463,32 +518,6 @@ RemoveDHCPStaticAddress() {
sed -i "/dhcp-host=${mac}.*/d" "${dhcpstaticconfig}" sed -i "/dhcp-host=${mac}.*/d" "${dhcpstaticconfig}"
} }
SetHostRecord() {
if [[ "${1}" == "-h" ]] || [[ "${1}" == "--help" ]]; then
echo "Usage: pihole -a hostrecord <domain> [IPv4-address],[IPv6-address]
Example: 'pihole -a hostrecord home.domain.com 192.168.1.1,2001:db8:a0b:12f0::1'
Add a name to the DNS associated to an IPv4/IPv6 address
Options:
\"\" Empty: Remove host record
-h, --help Show this help dialog"
exit 0
fi
if [[ -n "${args[3]}" ]]; then
change_setting "HOSTRECORD" "${args[2]},${args[3]}"
echo -e " ${TICK} Setting host record for ${args[2]} to ${args[3]}"
else
change_setting "HOSTRECORD" ""
echo -e " ${TICK} Removing host record"
fi
ProcessDNSSettings
# Restart dnsmasq to load new configuration
RestartDNS
}
SetAdminEmail() { SetAdminEmail() {
if [[ "${1}" == "-h" ]] || [[ "${1}" == "--help" ]]; then if [[ "${1}" == "-h" ]] || [[ "${1}" == "--help" ]]; then
echo "Usage: pihole -a email <address> echo "Usage: pihole -a email <address>
@ -502,6 +531,16 @@ Options:
fi fi
if [[ -n "${args[2]}" ]]; then if [[ -n "${args[2]}" ]]; then
# Sanitize email address in case of security issues
# Regex from https://stackoverflow.com/a/2138832/4065967
local regex
regex="^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\$"
if [[ ! "${args[2]}" =~ ${regex} ]]; then
echo -e " ${CROSS} Invalid email address"
exit 0
fi
change_setting "ADMIN_EMAIL" "${args[2]}" change_setting "ADMIN_EMAIL" "${args[2]}"
echo -e " ${TICK} Setting admin contact to ${args[2]}" echo -e " ${TICK} Setting admin contact to ${args[2]}"
else else
@ -597,6 +636,7 @@ SetPrivacyLevel() {
# Set privacy level. Minimum is 0, maximum is 4 # Set privacy level. Minimum is 0, maximum is 4
if [ "${args[2]}" -ge 0 ] && [ "${args[2]}" -le 4 ]; then if [ "${args[2]}" -ge 0 ] && [ "${args[2]}" -le 4 ]; then
changeFTLsetting "PRIVACYLEVEL" "${args[2]}" changeFTLsetting "PRIVACYLEVEL" "${args[2]}"
pihole restartdns reload-lists
fi fi
} }
@ -622,6 +662,28 @@ RemoveCustomDNSAddress() {
RestartDNS RestartDNS
} }
AddCustomCNAMERecord() {
echo -e " ${TICK} Adding custom CNAME record..."
domain="${args[2]}"
target="${args[3]}"
echo "cname=${domain},${target}" >> "${dnscustomcnamefile}"
# Restart dnsmasq to load new custom CNAME records
RestartDNS
}
RemoveCustomCNAMERecord() {
echo -e " ${TICK} Removing custom CNAME record..."
domain="${args[2]}"
target="${args[3]}"
sed -i "/cname=${domain},${target}/d" "${dnscustomcnamefile}"
# Restart dnsmasq to update removed custom CNAME records
RestartDNS
}
main() { main() {
args=("$@") args=("$@")
@ -640,12 +702,12 @@ main() {
"enabledhcp" ) EnableDHCP;; "enabledhcp" ) EnableDHCP;;
"disabledhcp" ) DisableDHCP;; "disabledhcp" ) DisableDHCP;;
"layout" ) SetWebUILayout;; "layout" ) SetWebUILayout;;
"theme" ) SetWebUITheme;;
"-h" | "--help" ) helpFunc;; "-h" | "--help" ) helpFunc;;
"privacymode" ) SetPrivacyMode;; "privacymode" ) SetPrivacyMode;;
"resolve" ) ResolutionSettings;; "resolve" ) ResolutionSettings;;
"addstaticdhcp" ) AddDHCPStaticAddress;; "addstaticdhcp" ) AddDHCPStaticAddress;;
"removestaticdhcp" ) RemoveDHCPStaticAddress;; "removestaticdhcp" ) RemoveDHCPStaticAddress;;
"-r" | "hostrecord" ) SetHostRecord "$3";;
"-e" | "email" ) SetAdminEmail "$3";; "-e" | "email" ) SetAdminEmail "$3";;
"-i" | "interface" ) SetListeningMode "$@";; "-i" | "interface" ) SetListeningMode "$@";;
"-t" | "teleporter" ) Teleporter;; "-t" | "teleporter" ) Teleporter;;
@ -655,6 +717,8 @@ main() {
"-l" | "privacylevel" ) SetPrivacyLevel;; "-l" | "privacylevel" ) SetPrivacyLevel;;
"addcustomdns" ) AddCustomDNSAddress;; "addcustomdns" ) AddCustomDNSAddress;;
"removecustomdns" ) RemoveCustomDNSAddress;; "removecustomdns" ) RemoveCustomDNSAddress;;
"addcustomcname" ) AddCustomCNAMERecord;;
"removecustomcname" ) RemoveCustomCNAMERecord;;
* ) helpFunc;; * ) helpFunc;;
esac esac

View file

@ -10,17 +10,18 @@ CREATE TABLE "group"
date_modified INTEGER NOT NULL DEFAULT (cast(strftime('%s', 'now') as int)), date_modified INTEGER NOT NULL DEFAULT (cast(strftime('%s', 'now') as int)),
description TEXT description TEXT
); );
INSERT INTO "group" (id,enabled,name) VALUES (0,1,'Unassociated'); INSERT INTO "group" (id,enabled,name,description) VALUES (0,1,'Default','The default group');
CREATE TABLE domainlist CREATE TABLE domainlist
( (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
type INTEGER NOT NULL DEFAULT 0, type INTEGER NOT NULL DEFAULT 0,
domain TEXT UNIQUE NOT NULL, domain TEXT NOT NULL,
enabled BOOLEAN NOT NULL DEFAULT 1, enabled BOOLEAN NOT NULL DEFAULT 1,
date_added INTEGER NOT NULL DEFAULT (cast(strftime('%s', 'now') as int)), date_added INTEGER NOT NULL DEFAULT (cast(strftime('%s', 'now') as int)),
date_modified INTEGER NOT NULL DEFAULT (cast(strftime('%s', 'now') as int)), date_modified INTEGER NOT NULL DEFAULT (cast(strftime('%s', 'now') as int)),
comment TEXT comment TEXT,
UNIQUE(domain, type)
); );
CREATE TABLE adlist CREATE TABLE adlist
@ -52,7 +53,7 @@ CREATE TABLE info
value TEXT NOT NULL value TEXT NOT NULL
); );
INSERT INTO "info" VALUES('version','11'); INSERT INTO "info" VALUES('version','12');
CREATE TABLE domain_audit CREATE TABLE domain_audit
( (
@ -167,7 +168,7 @@ CREATE TRIGGER tr_group_update AFTER UPDATE ON "group"
CREATE TRIGGER tr_group_zero AFTER DELETE ON "group" CREATE TRIGGER tr_group_zero AFTER DELETE ON "group"
BEGIN BEGIN
INSERT OR IGNORE INTO "group" (id,enabled,name) VALUES (0,1,'Unassociated'); INSERT OR IGNORE INTO "group" (id,enabled,name) VALUES (0,1,'Default');
END; END;
CREATE TRIGGER tr_domainlist_delete AFTER DELETE ON domainlist CREATE TRIGGER tr_domainlist_delete AFTER DELETE ON domainlist

View file

@ -1,8 +1,8 @@
#!/usr/bin/env bash #!/usr/bin/env bash
### BEGIN INIT INFO ### BEGIN INIT INFO
# Provides: pihole-FTL # Provides: pihole-FTL
# Required-Start: $remote_fs $syslog # Required-Start: $remote_fs $syslog $network
# Required-Stop: $remote_fs $syslog # Required-Stop: $remote_fs $syslog $network
# Default-Start: 2 3 4 5 # Default-Start: 2 3 4 5
# Default-Stop: 0 1 6 # Default-Stop: 0 1 6
# Short-Description: pihole-FTL daemon # Short-Description: pihole-FTL daemon
@ -10,7 +10,7 @@
### END INIT INFO ### END INIT INFO
FTLUSER=pihole FTLUSER=pihole
PIDFILE=/var/run/pihole-FTL.pid PIDFILE=/run/pihole-FTL.pid
get_pid() { get_pid() {
# First, try to obtain PID from PIDFILE # First, try to obtain PID from PIDFILE
@ -37,12 +37,12 @@ start() {
touch /var/log/pihole-FTL.log /var/log/pihole.log touch /var/log/pihole-FTL.log /var/log/pihole.log
touch /run/pihole-FTL.pid /run/pihole-FTL.port touch /run/pihole-FTL.pid /run/pihole-FTL.port
touch /etc/pihole/dhcp.leases touch /etc/pihole/dhcp.leases
mkdir -p /var/run/pihole mkdir -p /run/pihole
mkdir -p /var/log/pihole mkdir -p /var/log/pihole
chown pihole:pihole /var/run/pihole /var/log/pihole chown pihole:pihole /run/pihole /var/log/pihole
# Remove possible leftovers from previous pihole-FTL processes # Remove possible leftovers from previous pihole-FTL processes
rm -f /dev/shm/FTL-* 2> /dev/null rm -f /dev/shm/FTL-* 2> /dev/null
rm /var/run/pihole/FTL.sock 2> /dev/null rm /run/pihole/FTL.sock 2> /dev/null
# Ensure that permissions are set so that pihole-FTL can edit all necessary files # Ensure that permissions are set so that pihole-FTL can edit all necessary files
chown pihole:pihole /run/pihole-FTL.pid /run/pihole-FTL.port chown pihole:pihole /run/pihole-FTL.pid /run/pihole-FTL.port
chown pihole:pihole /etc/pihole /etc/pihole/dhcp.leases 2> /dev/null chown pihole:pihole /etc/pihole /etc/pihole/dhcp.leases 2> /dev/null
@ -50,7 +50,7 @@ start() {
chmod 0644 /var/log/pihole-FTL.log /run/pihole-FTL.pid /run/pihole-FTL.port /var/log/pihole.log chmod 0644 /var/log/pihole-FTL.log /run/pihole-FTL.pid /run/pihole-FTL.port /var/log/pihole.log
# Chown database files to the user FTL runs as. We ignore errors as the files may not (yet) exist # Chown database files to the user FTL runs as. We ignore errors as the files may not (yet) exist
chown pihole:pihole /etc/pihole/pihole-FTL.db /etc/pihole/gravity.db 2> /dev/null chown 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 if setcap CAP_NET_BIND_SERVICE,CAP_NET_RAW,CAP_NET_ADMIN,CAP_SYS_NICE+eip "$(which pihole-FTL)"; then
su -s /bin/sh -c "/usr/bin/pihole-FTL" "$FTLUSER" su -s /bin/sh -c "/usr/bin/pihole-FTL" "$FTLUSER"
else else
echo "Warning: Starting pihole-FTL as root because setting capabilities is not supported on this system" echo "Warning: Starting pihole-FTL as root because setting capabilities is not supported on this system"

View file

@ -10,7 +10,7 @@
# #
# #
# This file is under source-control of the Pi-hole installation and update # This file is under source-control of the Pi-hole installation and update
# scripts, any changes made to this file will be overwritten when the softare # scripts, any changes made to this file will be overwritten when the software
# is updated or re-installed. Please make any changes to the appropriate crontab # is updated or re-installed. Please make any changes to the appropriate crontab
# or other cron file snippets. # or other cron file snippets.

View file

@ -15,7 +15,7 @@ _pihole() {
COMPREPLY=( $(compgen -W "${opts_lists}" -- ${cur}) ) COMPREPLY=( $(compgen -W "${opts_lists}" -- ${cur}) )
;; ;;
"admin") "admin")
opts_admin="celsius email fahrenheit hostrecord interface kelvin password privacylevel" opts_admin="celsius email fahrenheit interface kelvin password privacylevel"
COMPREPLY=( $(compgen -W "${opts_admin}" -- ${cur}) ) COMPREPLY=( $(compgen -W "${opts_admin}" -- ${cur}) )
;; ;;
"checkout") "checkout")

View file

@ -6,45 +6,46 @@
* Please see LICENSE file for your rights under this license. */ * Please see LICENSE file for your rights under this license. */
/* Text Customisation Options ======> */ /* Text Customisation Options ======> */
.title:before { content: "Website Blocked"; } .title::before { content: "Website Blocked"; }
.altBtn:before { content: "Why am I here?"; } .altBtn::before { content: "Why am I here?"; }
.linkPH:before { content: "About Pi-hole"; } .linkPH::before { content: "About Pi-hole"; }
.linkEmail:before { content: "Contact Admin"; } .linkEmail::before { content: "Contact Admin"; }
#bpOutput.add:before { content: "Info"; } #bpOutput.add::before { content: "Info"; }
#bpOutput.add:after { content: "The domain is being whitelisted..."; } #bpOutput.add::after { content: "The domain is being whitelisted..."; }
#bpOutput.error:before, .unhandled:before { content: "Error"; } #bpOutput.error::before, .unhandled::before { content: "Error"; }
#bpOutput.unhandled:after { content: "An unhandled exception occured. This may happen when your browser is unable to load jQuery, or when the webserver is denying access to the Pi-hole API."; } #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::before { content: "Success"; }
#bpOutput.success:after { content: "Website has been whitelisted! You may need to flush your DNS cache"; } #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."; } .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."; } .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 "; } /* <a href="http://cname.com">cname.com</a> */ .cname::before { content: "This site is an alias for "; } /* <a href="http://cname.com">cname.com</a> */
.cname:after { content: ", which may be blocked by Pi-hole."; } .cname::after { content: ", which may be blocked by Pi-hole."; }
.blacklist:before { content: "Manually Blacklisted"; } .blacklist::before { content: "Manually Blacklisted"; }
.wildcard:before { content: "Manually Blacklisted by Wildcard"; } .wildcard::before { content: "Manually Blacklisted by Wildcard"; }
.noblock:before { content: "Not found on any Blacklist"; } .noblock::before { content: "Not found on any Blacklist"; }
#bpBlock:before { content: "Access to the following website has been denied:"; } #bpBlock::before { content: "Access to the following website has been denied:"; }
#bpFlag:before { content: "This is primarily due to being flagged as:"; } #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::before { content: "If you have an ongoing use for this website, please "; }
#bpHelpTxt a:before, #bpHelpTxt span:before { content: "ask the administrator"; } #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::after{ content: " of the Pi-hole on this network to have it whitelisted"; }
#bpBack:before { content: "Back to safety"; } #bpBack::before { content: "Back to safety"; }
#bpInfo:before { content: "Technical Info"; } #bpInfo::before { content: "Technical Info"; }
#bpFoundIn:before { content: "This site is found in "; } #bpFoundIn::before { content: "This site is found in "; }
#bpFoundIn span:after { content: " of "; } #bpFoundIn span::after { content: " of "; }
#bpFoundIn:after { content: " lists:"; } #bpFoundIn::after { content: " lists:"; }
#bpWhitelist:before { content: "Whitelist"; } #bpWhitelist::before { content: "Whitelist"; }
footer span:before { content: "Page generated on "; } footer span::before { content: "Page generated on "; }
/* Hide whitelisting form entirely */ /* Hide whitelisting form entirely */
/* #bpWLButtons { display: none; } */ /* #bpWLButtons { display: none; } */
/* Text Customisation Options <=============================== */ /* Text Customisation Options <=============================== */
/* http://necolas.github.io/normalize.css ======> */ /* http://necolas.github.io/normalize.css ======> */
@ -120,14 +121,20 @@ textarea, input, button { outline: none; }
font-family: "Source Sans Pro"; font-family: "Source Sans Pro";
font-style: normal; font-style: normal;
font-weight: 400; 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-face {
font-family: "Source Sans Pro"; font-family: "Source Sans Pro";
font-style: normal; font-style: normal;
font-weight: 700; 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 { body {
@ -167,7 +174,7 @@ h1 a {
background-color: rgba(0, 0, 0, 0.1); background-color: rgba(0, 0, 0, 0.1);
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 2rem; font-size: 2rem;
font-weight: normal; font-weight: 400;
min-width: 230px; min-width: 230px;
text-align: center; text-align: center;
} }
@ -183,10 +190,11 @@ header #bpAlt label {
text-indent: 30px; text-indent: 30px;
} }
[type=checkbox][id$="Toggle"] { display: none; } [type="checkbox"][id$="Toggle"] { display: none; }
[type=checkbox][id$="Toggle"]:checked ~ #bpAbout, [type="checkbox"][id$="Toggle"]:checked ~ #bpAbout,
[type=checkbox][id$="Toggle"]:checked ~ #bpMoreInfo { [type="checkbox"][id$="Toggle"]:checked ~ #bpMoreInfo {
display: block; } display: block;
}
/* Click anywhere else on screen to hide #bpAbout */ /* Click anywhere else on screen to hide #bpAbout */
#bpAboutToggle:checked { #bpAboutToggle:checked {
@ -203,7 +211,7 @@ header #bpAlt label {
#bpAbout { #bpAbout {
background: #3c8dbc; background: #3c8dbc;
border-bottom-left-radius: 5px; border-bottom-left-radius: 5px;
border: 1px solid #FFF; border: 1px solid #fff;
border-right-width: 0; 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; box-sizing: border-box;
@ -269,8 +277,8 @@ main {
padding: 15px; padding: 15px;
} }
#bpOutput:before { #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; 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; display: block;
font-size: 1.8rem; font-size: 1.8rem;
text-indent: 15px; text-indent: 15px;
@ -281,8 +289,8 @@ main {
#bpOutput.error { background: #dd4b39; } #bpOutput.error { background: #dd4b39; }
.blockMsg, .flagMsg { .blockMsg, .flagMsg {
font: bold 1.8rem Consolas, Courier, monospace; font: 700 1.8rem Consolas, Courier, monospace;
padding: 5px 10px 10px 10px; padding: 5px 10px 10px;
text-indent: 15px; text-indent: 15px;
} }
@ -317,7 +325,7 @@ main {
/* Button hover dark overlay */ /* Button hover dark overlay */
.buttons *:not(input):not([disabled]):hover { .buttons *:not(input):not([disabled]):hover {
background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.1)); background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.1));
color: #FFF; color: #fff;
} }
/* Button active shadow inset */ /* Button active shadow inset */
@ -325,30 +333,32 @@ main {
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 colour */ /* Input border color */
.buttons *:not([disabled]):hover, .buttons input:focus { .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; } #bpButtons * { width: 50%; color: #fff; }
#bpBack { background-color: #00a65a; } #bpBack { background-color: #00a65a; }
#bpInfo { background-color: #3c8dbc; } #bpInfo { background-color: #3c8dbc; }
#bpWhitelist { background-color: #dd4b39; } #bpWhitelist { background-color: #dd4b39; }
#blockpage .buttons [type=password][disabled] { color: rgba(0,0,0,1); } #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 [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"]:-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; } } @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; } #bpMoreInfo { display: none; margin-top: 10px; }
#bpQueryOutput { #bpQueryOutput {
font-size: 1.2rem; font-size: 1.2rem;
line-height: 1.65rem; line-height: 1.65rem;
margin: 5px 0 0 0; margin: 5px 0 0;
overflow: auto; overflow: auto;
padding: 0 5px; padding: 0 5px;
-webkit-overflow-scrolling: touch; -webkit-overflow-scrolling: touch;
@ -373,7 +383,7 @@ footer {
/* Responsive Content */ /* Responsive Content */
@media only screen and (max-width: 500px) { @media only screen and (max-width: 500px) {
h1 a { font-size: 1.8rem; min-width: 170px; } h1 a { font-size: 1.8rem; min-width: 170px; }
footer span:before { content: "Generated "; } footer span::before { content: "Generated "; }
footer span { display: block; } footer span { display: block; }
} }

View file

@ -46,7 +46,7 @@
#resolv-file= #resolv-file=
# By default, dnsmasq will send queries to any of the upstream # By default, dnsmasq will send queries to any of the upstream
# servers it knows about and tries to favour servers to are known # servers it knows about and tries to favor servers to are known
# to be up. Uncommenting this forces dnsmasq to try each query # to be up. Uncommenting this forces dnsmasq to try each query
# with each server strictly in the order they appear in # with each server strictly in the order they appear in
# /etc/resolv.conf # /etc/resolv.conf
@ -189,7 +189,7 @@
# add names to the DNS for the IPv6 address of SLAAC-configured dual-stack # add names to the DNS for the IPv6 address of SLAAC-configured dual-stack
# hosts. Use the DHCPv4 lease to derive the name, network segment and # hosts. Use the DHCPv4 lease to derive the name, network segment and
# MAC address and assume that the host will also have an # MAC address and assume that the host will also have an
# IPv6 address calculated using the SLAAC alogrithm. # IPv6 address calculated using the SLAAC algorithm.
#dhcp-range=1234::, ra-names #dhcp-range=1234::, ra-names
# Do Router Advertisements, BUT NOT DHCP for this subnet. # Do Router Advertisements, BUT NOT DHCP for this subnet.
@ -210,7 +210,7 @@
#dhcp-range=1234::, ra-stateless, ra-names #dhcp-range=1234::, ra-stateless, ra-names
# Do router advertisements for all subnets where we're doing DHCPv6 # Do router advertisements for all subnets where we're doing DHCPv6
# Unless overriden by ra-stateless, ra-names, et al, the router # Unless overridden by ra-stateless, ra-names, et al, the router
# advertisements will have the M and O bits set, so that the clients # advertisements will have the M and O bits set, so that the clients
# get addresses and configuration from DHCPv6, and the A bit reset, so the # get addresses and configuration from DHCPv6, and the A bit reset, so the
# clients don't use SLAAC addresses. # clients don't use SLAAC addresses.
@ -281,7 +281,7 @@
# Give a fixed IPv6 address and name to client with # Give a fixed IPv6 address and name to client with
# DUID 00:01:00:01:16:d2:83:fc:92:d4:19:e2:d8:b2 # DUID 00:01:00:01:16:d2:83:fc:92:d4:19:e2:d8:b2
# Note the MAC addresses CANNOT be used to identify DHCPv6 clients. # Note the MAC addresses CANNOT be used to identify DHCPv6 clients.
# Note also the they [] around the IPv6 address are obilgatory. # Note also the they [] around the IPv6 address are obligatory.
#dhcp-host=id:00:01:00:01:16:d2:83:fc:92:d4:19:e2:d8:b2, fred, [1234::5] #dhcp-host=id:00:01:00:01:16:d2:83:fc:92:d4:19:e2:d8:b2, fred, [1234::5]
# Ignore any clients which are not specified in dhcp-host lines # Ignore any clients which are not specified in dhcp-host lines
@ -404,14 +404,14 @@
#dhcp-option=vendor:MSFT,2,1i #dhcp-option=vendor:MSFT,2,1i
# Send the Encapsulated-vendor-class ID needed by some configurations of # Send the Encapsulated-vendor-class ID needed by some configurations of
# Etherboot to allow is to recognise the DHCP server. # Etherboot to allow is to recognize the DHCP server.
#dhcp-option=vendor:Etherboot,60,"Etherboot" #dhcp-option=vendor:Etherboot,60,"Etherboot"
# Send options to PXELinux. Note that we need to send the options even # Send options to PXELinux. Note that we need to send the options even
# though they don't appear in the parameter request list, so we need # though they don't appear in the parameter request list, so we need
# to use dhcp-option-force here. # to use dhcp-option-force here.
# See http://syslinux.zytor.com/pxe.php#special for details. # See http://syslinux.zytor.com/pxe.php#special for details.
# Magic number - needed before anything else is recognised # Magic number - needed before anything else is recognized
#dhcp-option-force=208,f1:00:74:7e #dhcp-option-force=208,f1:00:74:7e
# Configuration file name # Configuration file name
#dhcp-option-force=209,configs/common #dhcp-option-force=209,configs/common

View file

@ -6,8 +6,8 @@
* This file is copyright under the latest version of the EUPL. * This file is copyright under the latest version of the EUPL.
* Please see LICENSE file for your rights under this license. */ * Please see LICENSE file for your rights under this license. */
// Sanitise HTTP_HOST output // Sanitize SERVER_NAME output
$serverName = htmlspecialchars($_SERVER["HTTP_HOST"]); $serverName = htmlspecialchars($_SERVER["SERVER_NAME"]);
// Remove external ipv6 brackets if any // Remove external ipv6 brackets if any
$serverName = preg_replace('/^\[(.*)\]$/', '${1}', $serverName); $serverName = preg_replace('/^\[(.*)\]$/', '${1}', $serverName);
@ -41,7 +41,7 @@ $validExtTypes = array("asp", "htm", "html", "php", "rss", "xml", "");
$currentUrlExt = pathinfo($_SERVER["REQUEST_URI"], PATHINFO_EXTENSION); $currentUrlExt = pathinfo($_SERVER["REQUEST_URI"], PATHINFO_EXTENSION);
// Set mobile friendly viewport // Set mobile friendly viewport
$viewPort = '<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>'; $viewPort = '<meta name="viewport" content="width=device-width, initial-scale=1">';
// Set response header // Set response header
function setHeader($type = "x") { function setHeader($type = "x") {
@ -50,16 +50,29 @@ function setHeader($type = "x") {
} }
// Determine block page type // Determine block page type
if ($serverName === "pi.hole") { if ($serverName === "pi.hole"
|| (!empty($_SERVER["VIRTUAL_HOST"]) && $serverName === $_SERVER["VIRTUAL_HOST"])) {
// Redirect to Web Interface // Redirect to Web Interface
exit(header("Location: /admin")); exit(header("Location: /admin"));
} elseif (filter_var($serverName, FILTER_VALIDATE_IP) || in_array($serverName, $authorizedHosts)) { } elseif (filter_var($serverName, FILTER_VALIDATE_IP) || in_array($serverName, $authorizedHosts)) {
// Set Splash Page output // Set Splash Page output
$splashPage = " $splashPage = "
<html><head> <!doctype html>
<html lang='en'>
<head>
<meta charset='utf-8'>
$viewPort $viewPort
<link rel='stylesheet' href='/pihole/blockingpage.css' type='text/css'/> <title> $serverName</title>
</head><body id='splashpage'><img src='/admin/img/logo.svg'/><br/>Pi-<b>hole</b>: Your black hole for Internet advertisements<br><a href='/admin'>Did you mean to go to the admin panel?</a></body></html> <link rel='stylesheet' href='pihole/blockingpage.css'>
<link rel='shortcut icon' href='admin/img/favicons/favicon.ico' type='image/x-icon'>
</head>
<body id='splashpage'>
<img src='admin/img/logo.svg' alt='Pi-hole logo' width='256' height='377'>
<br>
<p>Pi-<strong>hole</strong>: Your black hole for Internet advertisements</p>
<a href='/admin'>Did you mean to go to the admin panel?</a>
</body>
</html>
"; ";
// Set splash/landing page based off presence of $landPage // Set splash/landing page based off presence of $landPage
@ -68,25 +81,42 @@ if ($serverName === "pi.hole") {
// Unset variables so as to not be included in $landPage // Unset variables so as to not be included in $landPage
unset($serverName, $svPasswd, $svEmail, $authorizedHosts, $validExtTypes, $currentUrlExt, $viewPort); unset($serverName, $svPasswd, $svEmail, $authorizedHosts, $validExtTypes, $currentUrlExt, $viewPort);
// Render splash/landing page when directly browsing via IP or authorised hostname // Render splash/landing page when directly browsing via IP or authorized hostname
exit($renderPage); exit($renderPage);
} elseif ($currentUrlExt === "js") { } 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."'); exit(setHeader("js").'var x = "Pi-hole: A black hole for Internet advertisements."');
} elseif (strpos($_SERVER["REQUEST_URI"], "?") !== FALSE && isset($_SERVER["HTTP_REFERER"])) { } elseif (strpos($_SERVER["REQUEST_URI"], "?") !== FALSE && isset($_SERVER["HTTP_REFERER"])) {
// Serve blank image upon receiving REQUEST_URI w/ query string & HTTP_REFERRER // Serve blank image upon receiving REQUEST_URI w/ query string & HTTP_REFERRER
// e.g: An iframe of a blocked domain // e.g: An iframe of a blocked domain
exit(setHeader().'<html> exit(setHeader().'<!doctype html>
<head><script>window.close();</script></head> <html lang="en">
<body><img src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs="></body> <head>
<meta charset="utf-8"><script>window.close();</script>
</head>
<body>
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=">
</body>
</html>'); </html>');
} elseif (!in_array($currentUrlExt, $validExtTypes) || substr_count($_SERVER["REQUEST_URI"], "?")) { } elseif (!in_array($currentUrlExt, $validExtTypes) || substr_count($_SERVER["REQUEST_URI"], "?")) {
// Serve SVG upon receiving non $validExtTypes URL extension or query string // 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 // 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 // QoL addition: Allow the SVG to be clicked on in order to quickly show the full Block Page
$blockImg = '<a href="/"><svg xmlns="http://www.w3.org/2000/svg" width="110" height="16"><defs><style>a {text-decoration: none;} circle {stroke: rgba(152,2,2,0.5); fill: none; stroke-width: 2;} rect {fill: rgba(152,2,2,0.5);} text {opacity: 0.3; font: 11px Arial;}</style></defs><circle cx="8" cy="8" r="7"/><rect x="10.3" y="-6" width="2" height="12" transform="rotate(45)"/><text x="19.3" y="12">Blocked by Pi-hole</text></svg></a>'; $blockImg = '<a href="/">
exit(setHeader()."<html> <svg xmlns="http://www.w3.org/2000/svg" width="110" height="16">
<head>$viewPort</head> <circle cx="8" cy="8" r="7" fill="none" stroke="rgba(152,2,2,.5)" stroke-width="2"/>
<path fill="rgba(152,2,2,.5)" d="M11.526 3.04l1.414 1.415-8.485 8.485-1.414-1.414z"/>
<text x="19.3" y="12" opacity=".3" style="font:11px Arial">
Blocked by Pi-hole
</text>
</svg>
</a>';
exit(setHeader()."<!doctype html>
<html lang='en'>
<head>
<meta charset='utf-8'>
$viewPort
</head>
<body>$blockImg</body> <body>$blockImg</body>
</html>"); </html>");
} }
@ -131,7 +161,12 @@ ini_set("default_socket_timeout", 3);
function queryAds($serverName) { function queryAds($serverName) {
// Determine the time it takes while querying adlists // Determine the time it takes while querying adlists
$preQueryTime = microtime(true)-$_SERVER["REQUEST_TIME_FLOAT"]; $preQueryTime = microtime(true)-$_SERVER["REQUEST_TIME_FLOAT"];
$queryAds = file("http://127.0.0.1/admin/scripts/pi-hole/php/queryads.php?domain=$serverName&bp", FILE_IGNORE_NEW_LINES); $queryAdsURL = sprintf(
"http://127.0.0.1:%s/admin/scripts/pi-hole/php/queryads.php?domain=%s&bp",
$_SERVER["SERVER_PORT"],
$serverName
);
$queryAds = file($queryAdsURL, FILE_IGNORE_NEW_LINES);
$queryAds = array_values(array_filter(preg_replace("/data:\s+/", "", $queryAds))); $queryAds = array_values(array_filter(preg_replace("/data:\s+/", "", $queryAds)));
$queryTime = sprintf("%.0f", (microtime(true)-$_SERVER["REQUEST_TIME_FLOAT"]) - $preQueryTime); $queryTime = sprintf("%.0f", (microtime(true)-$_SERVER["REQUEST_TIME_FLOAT"]) - $preQueryTime);
@ -209,12 +244,12 @@ $phVersion = exec("cd /etc/.pihole/ && git describe --long --tags");
if (explode("-", $phVersion)[1] != "0") if (explode("-", $phVersion)[1] != "0")
$execTime = microtime(true)-$_SERVER["REQUEST_TIME_FLOAT"]; $execTime = microtime(true)-$_SERVER["REQUEST_TIME_FLOAT"];
// Please Note: Text is added via CSS to allow an admin to provide a localised // Please Note: Text is added via CSS to allow an admin to provide a localized
// language without the need to edit this file // language without the need to edit this file
setHeader(); setHeader();
?> ?>
<!DOCTYPE html> <!doctype html>
<!-- Pi-hole: A black hole for Internet advertisements <!-- Pi-hole: A black hole for Internet advertisements
* (c) 2017 Pi-hole, LLC (https://pi-hole.net) * (c) 2017 Pi-hole, LLC (https://pi-hole.net)
* Network-wide ad blocking via your own hardware. * Network-wide ad blocking via your own hardware.
@ -222,14 +257,14 @@ setHeader();
* This file is copyright under the latest version of the EUPL. --> * This file is copyright under the latest version of the EUPL. -->
<html> <html>
<head> <head>
<meta charset="UTF-8"> <meta charset="utf-8">
<?=$viewPort ?> <?=$viewPort ?>
<meta name="robots" content="noindex,nofollow"/> <meta name="robots" content="noindex,nofollow">
<meta http-equiv="x-dns-prefetch-control" content="off"> <meta http-equiv="x-dns-prefetch-control" content="off">
<link rel="shortcut icon" href="//pi.hole/admin/img/favicon.png" type="image/x-icon"/> <link rel="stylesheet" href="pihole/blockingpage.css">
<link rel="stylesheet" href="//pi.hole/pihole/blockingpage.css" type="text/css"/> <link rel="shortcut icon" href="admin/img/favicons/favicon.ico" type="image/x-icon">
<title> <?=$serverName ?></title> <title> <?=$serverName ?></title>
<script src="//pi.hole/admin/scripts/vendor/jquery.min.js"></script> <script src="admin/scripts/vendor/jquery.min.js"></script>
<script> <script>
window.onload = function () { window.onload = function () {
<?php <?php
@ -261,10 +296,10 @@ setHeader();
</h1> </h1>
<div class="spc"></div> <div class="spc"></div>
<input id="bpAboutToggle" type="checkbox"/> <input id="bpAboutToggle" type="checkbox">
<div id="bpAbout"> <div id="bpAbout">
<div class="aboutPH"> <div class="aboutPH">
<div class="aboutImg"/></div> <div class="aboutImg"></div>
<p>Open Source Ad Blocker <p>Open Source Ad Blocker
<small>Designed for Raspberry Pi</small> <small>Designed for Raspberry Pi</small>
</p> </p>
@ -301,8 +336,9 @@ setHeader();
<pre id='bpQueryOutput'><?php if ($featuredTotal > 0) foreach ($queryResults as $num => $value) { echo "<span>[$num]:</span>$adlistsUrls[$num]\n"; } ?></pre> <pre id='bpQueryOutput'><?php if ($featuredTotal > 0) foreach ($queryResults as $num => $value) { echo "<span>[$num]:</span>$adlistsUrls[$num]\n"; } ?></pre>
<form id="bpWLButtons" class="buttons"> <form id="bpWLButtons" class="buttons">
<input id="bpWLDomain" type="text" value="<?=$serverName ?>" disabled/> <input id="bpWLDomain" type="text" value="<?=$serverName ?>" disabled>
<input id="bpWLPassword" type="password" placeholder="Javascript disabled" disabled/><button id="bpWhitelist" type="button" disabled></button> <input id="bpWLPassword" type="password" placeholder="JavaScript disabled" disabled>
<button id="bpWhitelist" type="button" disabled></button>
</form> </form>
</div> </div>
</main> </main>

View file

@ -30,7 +30,7 @@ server.document-root = "/var/www/html"
server.error-handler-404 = "/pihole/index.php" server.error-handler-404 = "/pihole/index.php"
server.upload-dirs = ( "/var/cache/lighttpd/uploads" ) server.upload-dirs = ( "/var/cache/lighttpd/uploads" )
server.errorlog = "/var/log/lighttpd/error.log" server.errorlog = "/var/log/lighttpd/error.log"
server.pid-file = "/var/run/lighttpd.pid" server.pid-file = "/run/lighttpd.pid"
server.username = "www-data" server.username = "www-data"
server.groupname = "www-data" server.groupname = "www-data"
server.port = 80 server.port = 80
@ -42,17 +42,44 @@ url.access-deny = ( "~", ".inc", ".md", ".yml", ".ini" )
static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" ) static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" )
compress.cache-dir = "/var/cache/lighttpd/compress/" compress.cache-dir = "/var/cache/lighttpd/compress/"
compress.filetype = ( "application/javascript", "text/css", "text/html", "text/plain" ) compress.filetype = (
"application/json",
"application/vnd.ms-fontobject",
"application/xml",
"font/eot",
"font/opentype",
"font/otf",
"font/ttf",
"image/bmp",
"image/svg+xml",
"image/vnd.microsoft.icon",
"image/x-icon",
"text/css",
"text/html",
"text/javascript",
"text/plain",
"text/xml"
)
mimetype.assign = ( ".png" => "image/png", mimetype.assign = (
".jpg" => "image/jpeg", ".ico" => "image/x-icon",
".jpeg" => "image/jpeg", ".jpeg" => "image/jpeg",
".html" => "text/html", ".jpg" => "image/jpeg",
".png" => "image/png",
".svg" => "image/svg+xml",
".css" => "text/css; charset=utf-8", ".css" => "text/css; charset=utf-8",
".js" => "application/javascript", ".html" => "text/html; charset=utf-8",
".json" => "application/json", ".js" => "text/javascript; charset=utf-8",
".txt" => "text/plain", ".json" => "application/json; charset=utf-8",
".svg" => "image/svg+xml" ) ".map" => "application/json; charset=utf-8",
".txt" => "text/plain; charset=utf-8",
".eot" => "application/vnd.ms-fontobject",
".otf" => "font/otf",
".ttc" => "font/collection",
".ttf" => "font/ttf",
".woff" => "font/woff",
".woff2" => "font/woff2"
)
# default listening port for IPv6 falls back to the IPv4 port # default listening port for IPv6 falls back to the IPv4 port
include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port
@ -69,7 +96,7 @@ $HTTP["url"] =~ "^/admin/" {
"X-Frame-Options" => "DENY" "X-Frame-Options" => "DENY"
) )
$HTTP["url"] =~ ".ttf$" { $HTTP["url"] =~ "\.(eot|otf|tt[cf]|woff2?)$" {
# Allow Block Page access to local fonts # Allow Block Page access to local fonts
setenv.add-response-header = ( "Access-Control-Allow-Origin" => "*" ) setenv.add-response-header = ( "Access-Control-Allow-Origin" => "*" )
} }
@ -80,6 +107,9 @@ $HTTP["url"] =~ "^/admin/\.(.*)" {
url.access-deny = ("") url.access-deny = ("")
} }
# Default expire header
expire.url = ( "" => "access plus 0 seconds" )
# Add user chosen options held in external file # Add user chosen options held in external file
# This uses include_shell instead of an include wildcard for compatibility # This uses include_shell instead of an include wildcard for compatibility
include_shell "cat external.conf 2>/dev/null" include_shell "cat external.conf 2>/dev/null"

View file

@ -2,7 +2,7 @@
# (c) 2017 Pi-hole, LLC (https://pi-hole.net) # (c) 2017 Pi-hole, LLC (https://pi-hole.net)
# Network-wide ad blocking via your own hardware. # Network-wide ad blocking via your own hardware.
# #
# lighttpd config for Pi-hole # Lighttpd config for Pi-hole
# #
# This file is copyright under the latest version of the EUPL. # This file is copyright under the latest version of the EUPL.
# Please see LICENSE file for your rights under this license. # Please see LICENSE file for your rights under this license.
@ -18,9 +18,9 @@
server.modules = ( server.modules = (
"mod_access", "mod_access",
"mod_auth", "mod_auth",
"mod_expire",
"mod_fastcgi", "mod_fastcgi",
"mod_accesslog", "mod_accesslog",
"mod_expire",
"mod_compress", "mod_compress",
"mod_redirect", "mod_redirect",
"mod_setenv", "mod_setenv",
@ -31,39 +31,65 @@ server.document-root = "/var/www/html"
server.error-handler-404 = "/pihole/index.php" server.error-handler-404 = "/pihole/index.php"
server.upload-dirs = ( "/var/cache/lighttpd/uploads" ) server.upload-dirs = ( "/var/cache/lighttpd/uploads" )
server.errorlog = "/var/log/lighttpd/error.log" server.errorlog = "/var/log/lighttpd/error.log"
server.pid-file = "/var/run/lighttpd.pid" server.pid-file = "/run/lighttpd.pid"
server.username = "lighttpd" server.username = "lighttpd"
server.groupname = "lighttpd" server.groupname = "lighttpd"
server.port = 80 server.port = 80
accesslog.filename = "/var/log/lighttpd/access.log" accesslog.filename = "/var/log/lighttpd/access.log"
accesslog.format = "%{%s}t|%V|%r|%s|%b" accesslog.format = "%{%s}t|%V|%r|%s|%b"
index-file.names = ( "index.php", "index.html", "index.lighttpd.html" ) index-file.names = ( "index.php", "index.html", "index.lighttpd.html" )
url.access-deny = ( "~", ".inc", ".md", ".yml", ".ini" ) url.access-deny = ( "~", ".inc", ".md", ".yml", ".ini" )
static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" ) static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" )
compress.cache-dir = "/var/cache/lighttpd/compress/" compress.cache-dir = "/var/cache/lighttpd/compress/"
compress.filetype = ( "application/javascript", "text/css", "text/html", "text/plain" ) compress.filetype = (
"application/json",
"application/vnd.ms-fontobject",
"application/xml",
"font/eot",
"font/opentype",
"font/otf",
"font/ttf",
"image/bmp",
"image/svg+xml",
"image/vnd.microsoft.icon",
"image/x-icon",
"text/css",
"text/html",
"text/javascript",
"text/plain",
"text/xml"
)
mimetype.assign = ( ".png" => "image/png", mimetype.assign = (
".jpg" => "image/jpeg", ".ico" => "image/x-icon",
".jpeg" => "image/jpeg", ".jpeg" => "image/jpeg",
".html" => "text/html", ".jpg" => "image/jpeg",
".png" => "image/png",
".svg" => "image/svg+xml",
".css" => "text/css; charset=utf-8", ".css" => "text/css; charset=utf-8",
".js" => "application/javascript", ".html" => "text/html; charset=utf-8",
".json" => "application/json", ".js" => "text/javascript; charset=utf-8",
".txt" => "text/plain", ".json" => "application/json; charset=utf-8",
".svg" => "image/svg+xml" ) ".map" => "application/json; charset=utf-8",
".txt" => "text/plain; charset=utf-8",
".eot" => "application/vnd.ms-fontobject",
".otf" => "font/otf",
".ttc" => "font/collection",
".ttf" => "font/ttf",
".woff" => "font/woff",
".woff2" => "font/woff2"
)
# default listening port for IPv6 falls back to the IPv4 port # default listening port for IPv6 falls back to the IPv4 port
#include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port #include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port
#include_shell "/usr/share/lighttpd/create-mime.assign.pl" #include_shell "/usr/share/lighttpd/create-mime.assign.pl"
#include_shell "/usr/share/lighttpd/include-conf-enabled.pl" #include_shell "/usr/share/lighttpd/include-conf-enabled.pl"
fastcgi.server = ( ".php" => fastcgi.server = (
( "localhost" => ".php" => (
( "localhost" => (
"socket" => "/tmp/php-fastcgi.socket", "socket" => "/tmp/php-fastcgi.socket",
"bin-path" => "/usr/bin/php-cgi" "bin-path" => "/usr/bin/php-cgi"
) )
@ -78,7 +104,7 @@ $HTTP["url"] =~ "^/admin/" {
"X-Frame-Options" => "DENY" "X-Frame-Options" => "DENY"
) )
$HTTP["url"] =~ ".ttf$" { $HTTP["url"] =~ "\.(eot|otf|tt[cf]|woff2?)$" {
# Allow Block Page access to local fonts # Allow Block Page access to local fonts
setenv.add-response-header = ( "Access-Control-Allow-Origin" => "*" ) setenv.add-response-header = ( "Access-Control-Allow-Origin" => "*" )
} }
@ -89,6 +115,9 @@ $HTTP["url"] =~ "^/admin/\.(.*)" {
url.access-deny = ("") url.access-deny = ("")
} }
# Default expire header
expire.url = ( "" => "access plus 0 seconds" )
# Add user chosen options held in external file # Add user chosen options held in external file
# This uses include_shell instead of an include wildcard for compatibility # This uses include_shell instead of an include wildcard for compatibility
include_shell "cat external.conf 2>/dev/null" include_shell "cat external.conf 2>/dev/null"

View file

@ -72,8 +72,8 @@ useUpdateVars=false
adlistFile="/etc/pihole/adlists.list" adlistFile="/etc/pihole/adlists.list"
# Pi-hole needs an IP address; to begin, these variables are empty since we don't know what the IP is until # Pi-hole needs an IP address; to begin, these variables are empty since we don't know what the IP is until
# this script can run # this script can run
IPV4_ADDRESS="" IPV4_ADDRESS=${IPV4_ADDRESS}
IPV6_ADDRESS="" IPV6_ADDRESS=${IPV6_ADDRESS}
# By default, query logging is enabled and the dashboard is set to be installed # By default, query logging is enabled and the dashboard is set to be installed
QUERY_LOGGING=true QUERY_LOGGING=true
INSTALL_WEB_INTERFACE=true INSTALL_WEB_INTERFACE=true
@ -174,6 +174,61 @@ is_command() {
command -v "${check_command}" >/dev/null 2>&1 command -v "${check_command}" >/dev/null 2>&1
} }
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_pretty detected_os detected_version display_warning
remote_os_domain="versions.pi-hole.net"
valid_os=false
valid_version=false
display_warning=true
detected_os_pretty=$(cat /etc/*release | grep PRETTY_NAME | cut -d '=' -f2- | tr -d '"')
detected_os="${detected_os_pretty%% *}"
detected_version=$(cat /etc/*release | grep VERSION_ID | cut -d '=' -f2- | tr -d '"')
IFS=" " read -r -a supportedOS < <(dig +short -t txt ${remote_os_domain} | tr -d '"')
for i in "${supportedOS[@]}"
do
os_part=$(echo "$i" | cut -d '=' -f1)
versions_part=$(echo "$i" | cut -d '=' -f2-)
if [[ "${detected_os}" =~ ${os_part} ]]; then
valid_os=true
IFS="," read -r -a supportedVer <<<"${versions_part}"
for x in "${supportedVer[@]}"
do
if [[ "${detected_version}" =~ $x ]];then
valid_version=true
break
fi
done
break
fi
done
if [ "$valid_os" = true ] && [ "$valid_version" = true ]; then
display_warning=false
fi
if [ "$display_warning" = true ] && [ "$PIHOLE_SKIP_OS_CHECK" != true ]; then
printf " %b %bUnsupported OS detected%b\\n" "${CROSS}" "${COL_LIGHT_RED}" "${COL_NC}"
printf " https://docs.pi-hole.net/main/prerequesites/#supported-operating-systems\\n"
printf "\\n"
printf " This check can be skipped by setting the environment variable %bPIHOLE_SKIP_OS_CHECK%b to %btrue%b\\n" "${COL_LIGHT_RED}" "${COL_NC}" "${COL_LIGHT_RED}" "${COL_NC}"
printf " e.g: export PIHOLE_SKIP_OS_CHECK=true\\n"
printf " By setting this variable to true you acknowledge there may be issues with Pi-hole during or after the install\\n"
printf " If that is the case, you can feel free to ask the community on Discourse with the %bCommunity Help%b category:\\n" "${COL_LIGHT_RED}" "${COL_NC}"
printf " https://discourse.pi-hole.net/c/bugs-problems-issues/community-help/\\n"
exit 1
elif [ "$display_warning" = true ] && [ "$PIHOLE_SKIP_OS_CHECK" = true ]; then
printf " %b %bUnsupported OS detected%b. PIHOLE_SKIP_OS_CHECK env variable set to true - installer will continue\\n" "${INFO}" "${COL_LIGHT_RED}" "${COL_NC}"
else
printf " %b %bSupported OS detected%b\\n" "${TICK}" "${COL_LIGHT_GREEN}" "${COL_NC}"
fi
}
# Compatibility # Compatibility
distro_check() { distro_check() {
# If apt-get is installed, then we know it's part of the Debian family # If apt-get is installed, then we know it's part of the Debian family
@ -184,7 +239,7 @@ if is_command apt-get ; then
# A variable to store the command used to update the package cache # A variable to store the command used to update the package cache
UPDATE_PKG_CACHE="${PKG_MANAGER} update" UPDATE_PKG_CACHE="${PKG_MANAGER} update"
# An array for something... # An array for something...
PKG_INSTALL=("${PKG_MANAGER}" --yes --no-install-recommends install) PKG_INSTALL=("${PKG_MANAGER}" -qq --no-install-recommends install)
# grep -c will return 1 retVal on 0 matches, block this throwing the set -e with an OR TRUE # grep -c will return 1 retVal on 0 matches, block this throwing the set -e with an OR TRUE
PKG_COUNT="${PKG_MANAGER} -s -o Debug::NoLocking=true upgrade | grep -c ^Inst || true" PKG_COUNT="${PKG_MANAGER} -s -o Debug::NoLocking=true upgrade | grep -c ^Inst || true"
# Some distros vary slightly so these fixes for dependencies may apply # Some distros vary slightly so these fixes for dependencies may apply
@ -192,8 +247,8 @@ if is_command apt-get ; then
APT_SOURCES="/etc/apt/sources.list" APT_SOURCES="/etc/apt/sources.list"
if awk 'BEGIN{a=1;b=0}/bionic main/{a=0}/bionic.*universe/{b=1}END{exit a + b}' ${APT_SOURCES}; then if awk 'BEGIN{a=1;b=0}/bionic main/{a=0}/bionic.*universe/{b=1}END{exit a + b}' ${APT_SOURCES}; then
if ! whiptail --defaultno --title "Dependencies Require Update to Allowed Repositories" --yesno "Would you like to enable 'universe' repository?\\n\\nThis repository is required by the following packages:\\n\\n- dhcpcd5" "${r}" "${c}"; then if ! whiptail --defaultno --title "Dependencies Require Update to Allowed Repositories" --yesno "Would you like to enable 'universe' repository?\\n\\nThis repository is required by the following packages:\\n\\n- dhcpcd5" "${r}" "${c}"; then
printf " %b Aborting installation: dependencies could not be installed.\\n" "${CROSS}" printf " %b Aborting installation: Dependencies could not be installed.\\n" "${CROSS}"
exit # exit the installer exit 1 # exit the installer
else else
printf " %b Enabling universe package repository for Ubuntu Bionic\\n" "${INFO}" printf " %b Enabling universe package repository for Ubuntu Bionic\\n" "${INFO}"
cp -p ${APT_SOURCES} ${APT_SOURCES}.backup # Backup current repo list cp -p ${APT_SOURCES} ${APT_SOURCES}.backup # Backup current repo list
@ -202,14 +257,18 @@ if is_command apt-get ; then
printf " %b Enabled %s\\n" "${TICK}" "'universe' repository" printf " %b Enabled %s\\n" "${TICK}" "'universe' repository"
fi fi
fi fi
# Debian 7 doesn't have iproute2 so if the dry run install is successful, # Update package cache. This is required already here to assure apt-cache calls have package lists available.
if "${PKG_MANAGER}" install --dry-run iproute2 > /dev/null 2>&1; then update_package_cache || exit 1
# we can install it # Debian 7 doesn't have iproute2 so check if it's available first
if apt-cache show iproute2 > /dev/null 2>&1; then
iproute_pkg="iproute2" iproute_pkg="iproute2"
# Otherwise, # Otherwise, check if iproute is available
else elif apt-cache show iproute > /dev/null 2>&1; then
# use iproute
iproute_pkg="iproute" iproute_pkg="iproute"
# Else print error and exit
else
printf " %b Aborting installation: iproute2 and iproute packages were not found in APT repository.\\n" "${CROSS}"
exit 1
fi fi
# Check for and determine version number (major and minor) of current php install # Check for and determine version number (major and minor) of current php install
if is_command php ; then if is_command php ; then
@ -224,30 +283,37 @@ if is_command apt-get ; then
# Check if installed php is v 7.0, or newer to determine packages to install # Check if installed php is v 7.0, or newer to determine packages to install
if [[ "$phpInsNewer" != true ]]; then if [[ "$phpInsNewer" != true ]]; then
# Prefer the php metapackage if it's there # Prefer the php metapackage if it's there
if "${PKG_MANAGER}" install --dry-run php > /dev/null 2>&1; then if apt-cache show php > /dev/null 2>&1; then
phpVer="php" phpVer="php"
# fall back on the php5 packages # Else fall back on the php5 package if it's there
else elif apt-cache show php5 > /dev/null 2>&1; then
phpVer="php5" phpVer="php5"
# Else print error and exit
else
printf " %b Aborting installation: No PHP packages were found in APT repository.\\n" "${CROSS}"
exit 1
fi fi
else else
# Newer php is installed, its common, cgi & sqlite counterparts are deps # Newer php is installed, its common, cgi & sqlite counterparts are deps
phpVer="php$phpInsMajor.$phpInsMinor" phpVer="php$phpInsMajor.$phpInsMinor"
fi fi
# We also need the correct version for `php-sqlite` (which differs across distros) # We also need the correct version for `php-sqlite` (which differs across distros)
if "${PKG_MANAGER}" install --dry-run "${phpVer}-sqlite3" > /dev/null 2>&1; then if apt-cache show "${phpVer}-sqlite3" > /dev/null 2>&1; then
phpSqlite="sqlite3" phpSqlite="sqlite3"
else elif apt-cache show "${phpVer}-sqlite" > /dev/null 2>&1; then
phpSqlite="sqlite" phpSqlite="sqlite"
else
printf " %b Aborting installation: No SQLite PHP module was found in APT repository.\\n" "${CROSS}"
exit 1
fi fi
# Since our install script is so large, we need several other programs to successfully get a machine provisioned # Since our install script is so large, we need several other programs to successfully get a machine provisioned
# These programs are stored in an array so they can be looped through later # These programs are stored in an array so they can be looped through later
INSTALLER_DEPS=(dhcpcd5 git "${iproute_pkg}" whiptail) INSTALLER_DEPS=(dhcpcd5 git "${iproute_pkg}" whiptail dnsutils)
# Pi-hole itself has several dependencies that also need to be installed # Pi-hole itself has several dependencies that also need to be installed
PIHOLE_DEPS=(cron curl dnsutils iputils-ping lsof netcat psmisc sudo unzip wget idn2 sqlite3 libcap2-bin dns-root-data resolvconf libcap2) PIHOLE_DEPS=(cron curl iputils-ping lsof netcat psmisc sudo unzip wget idn2 sqlite3 libcap2-bin dns-root-data libcap2)
# The Web dashboard has some that also need to be installed # The Web dashboard has some that also need to be installed
# It's useful to separate the two since our repos are also setup as "Core" code and "Web" code # It's useful to separate the two since our repos are also setup as "Core" code and "Web" code
PIHOLE_WEB_DEPS=(lighttpd "${phpVer}-common" "${phpVer}-cgi" "${phpVer}-${phpSqlite}" "${phpVer}-xml" "php-intl") PIHOLE_WEB_DEPS=(lighttpd "${phpVer}-common" "${phpVer}-cgi" "${phpVer}-${phpSqlite}" "${phpVer}-xml" "${phpVer}-intl")
# The Web server user, # The Web server user,
LIGHTTPD_USER="www-data" LIGHTTPD_USER="www-data"
# group, # group,
@ -281,12 +347,10 @@ elif is_command rpm ; then
PKG_MANAGER="yum" PKG_MANAGER="yum"
fi fi
# Fedora and family update cache on every PKG_INSTALL call, no need for a separate update.
UPDATE_PKG_CACHE=":"
PKG_INSTALL=("${PKG_MANAGER}" install -y) PKG_INSTALL=("${PKG_MANAGER}" install -y)
PKG_COUNT="${PKG_MANAGER} check-update | egrep '(.i686|.x86|.noarch|.arm|.src)' | wc -l" PKG_COUNT="${PKG_MANAGER} check-update | egrep '(.i686|.x86|.noarch|.arm|.src)' | wc -l"
INSTALLER_DEPS=(git iproute newt procps-ng which chkconfig) INSTALLER_DEPS=(git iproute newt procps-ng which chkconfig bind-utils)
PIHOLE_DEPS=(bind-utils cronie curl findutils nmap-ncat sudo unzip wget libidn2 psmisc sqlite libcap) PIHOLE_DEPS=(cronie curl findutils nmap-ncat sudo unzip libidn2 psmisc sqlite libcap)
PIHOLE_WEB_DEPS=(lighttpd lighttpd-fastcgi php-common php-cli php-pdo php-xml php-json php-intl) PIHOLE_WEB_DEPS=(lighttpd lighttpd-fastcgi php-common php-cli php-pdo php-xml php-json php-intl)
LIGHTTPD_USER="lighttpd" LIGHTTPD_USER="lighttpd"
LIGHTTPD_GROUP="lighttpd" LIGHTTPD_GROUP="lighttpd"
@ -426,11 +490,9 @@ make_repo() {
fi fi
# Clone the repo and return the return code from this command # Clone the repo and return the return code from this command
git clone -q --depth 20 "${remoteRepo}" "${directory}" &> /dev/null || return $? git clone -q --depth 20 "${remoteRepo}" "${directory}" &> /dev/null || return $?
# Data in the repositories is public anyway so we can make it readable by everyone (+r to keep executable permission if already set by git)
chmod -R a+rX "${directory}"
# Move into the directory that was passed as an argument # Move into the directory that was passed as an argument
pushd "${directory}" &> /dev/null || return 1 pushd "${directory}" &> /dev/null || return 1
# Check current branch. If it is master, then reset to the latest availible tag. # Check current branch. If it is master, then reset to the latest available tag.
# In case extra commits have been added after tagging/release (i.e in case of metadata updates/README.MD tweaks) # In case extra commits have been added after tagging/release (i.e in case of metadata updates/README.MD tweaks)
curBranch=$(git rev-parse --abbrev-ref HEAD) curBranch=$(git rev-parse --abbrev-ref HEAD)
if [[ "${curBranch}" == "master" ]]; then #If we're calling make_repo() then it should always be master, we may not need to check. if [[ "${curBranch}" == "master" ]]; then #If we're calling make_repo() then it should always be master, we may not need to check.
@ -438,7 +500,8 @@ make_repo() {
fi fi
# Show a colored message showing it's status # Show a colored message showing it's status
printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}" printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}"
# Data in the repositories is public anyway so we can make it readable by everyone (+r to keep executable permission if already set by git)
chmod -R a+rX "${directory}"
# Move back into the original directory # Move back into the original directory
popd &> /dev/null || return 1 popd &> /dev/null || return 1
return 0 return 0
@ -466,7 +529,7 @@ update_repo() {
git clean --quiet --force -d || true # Okay for already clean directory git clean --quiet --force -d || true # Okay for already clean directory
# Pull the latest commits # Pull the latest commits
git pull --quiet &> /dev/null || return $? git pull --quiet &> /dev/null || return $?
# Check current branch. If it is master, then reset to the latest availible tag. # Check current branch. If it is master, then reset to the latest available tag.
# In case extra commits have been added after tagging/release (i.e in case of metadata updates/README.MD tweaks) # In case extra commits have been added after tagging/release (i.e in case of metadata updates/README.MD tweaks)
curBranch=$(git rev-parse --abbrev-ref HEAD) curBranch=$(git rev-parse --abbrev-ref HEAD)
if [[ "${curBranch}" == "master" ]]; then if [[ "${curBranch}" == "master" ]]; then
@ -573,7 +636,7 @@ welcomeDialogs() {
whiptail --msgbox --backtitle "Welcome" --title "Pi-hole automated installer" "\\n\\nThis installer will transform your device into a network-wide ad blocker!" "${r}" "${c}" whiptail --msgbox --backtitle "Welcome" --title "Pi-hole automated installer" "\\n\\nThis installer will transform your device into a network-wide ad blocker!" "${r}" "${c}"
# Request that users donate if they enjoy the software since we all work on it in our free time # Request that users donate if they enjoy the software since we all work on it in our free time
whiptail --msgbox --backtitle "Plea" --title "Free and open source" "\\n\\nThe Pi-hole is free, but powered by your donations: http://pi-hole.net/donate" "${r}" "${c}" whiptail --msgbox --backtitle "Plea" --title "Free and open source" "\\n\\nThe Pi-hole is free, but powered by your donations: https://pi-hole.net/donate/" "${r}" "${c}"
# Explain the need for a static address # Explain the need for a static address
whiptail --msgbox --backtitle "Initiating network interface" --title "Static IP Needed" "\\n\\nThe Pi-hole is a SERVER so it needs a STATIC IP ADDRESS to function properly. whiptail --msgbox --backtitle "Initiating network interface" --title "Static IP Needed" "\\n\\nThe Pi-hole is a SERVER so it needs a STATIC IP ADDRESS to function properly.
@ -600,7 +663,7 @@ verifyFreeDiskSpace() {
printf " We were unable to determine available free disk space on this system.\\n" printf " We were unable to determine available free disk space on this system.\\n"
printf " You may override this check, however, it is not recommended.\\n" printf " You may override this check, however, it is not recommended.\\n"
printf " The option '%b--i_do_not_follow_recommendations%b' can override this.\\n" "${COL_LIGHT_RED}" "${COL_NC}" printf " The option '%b--i_do_not_follow_recommendations%b' can override this.\\n" "${COL_LIGHT_RED}" "${COL_NC}"
printf " e.g: curl -L https://install.pi-hole.net | bash /dev/stdin %b<option>%b\\n" "${COL_LIGHT_RED}" "${COL_NC}" printf " e.g: curl -sSL https://install.pi-hole.net | bash /dev/stdin %b<option>%b\\n" "${COL_LIGHT_RED}" "${COL_NC}"
# exit with an error code # exit with an error code
exit 1 exit 1
# If there is insufficient free disk space, # If there is insufficient free disk space,
@ -615,7 +678,7 @@ verifyFreeDiskSpace() {
printf " If this is a new install you may need to expand your disk\\n" printf " If this is a new install you may need to expand your disk\\n"
printf " Run 'sudo raspi-config', and choose the 'expand file system' option\\n" printf " Run 'sudo raspi-config', and choose the 'expand file system' option\\n"
printf " After rebooting, run this installation again\\n" printf " After rebooting, run this installation again\\n"
printf " e.g: curl -L https://install.pi-hole.net | bash\\n" printf " e.g: curl -sSL https://install.pi-hole.net | bash\\n"
fi fi
# Show there is not enough free space # Show there is not enough free space
printf "\\n %bInsufficient free space, exiting...%b\\n" "${COL_LIGHT_RED}" "${COL_NC}" printf "\\n %bInsufficient free space, exiting...%b\\n" "${COL_LIGHT_RED}" "${COL_NC}"
@ -665,7 +728,7 @@ chooseInterface() {
# Feed the available interfaces into this while loop # Feed the available interfaces into this while loop
done <<< "${availableInterfaces}" done <<< "${availableInterfaces}"
# The whiptail command that will be run, stored in a variable # The whiptail command that will be run, stored in a variable
chooseInterfaceCmd=(whiptail --separate-output --radiolist "Choose An Interface (press space to select)" "${r}" "${c}" "${interfaceCount}") chooseInterfaceCmd=(whiptail --separate-output --radiolist "Choose An Interface (press space to toggle selection)" "${r}" "${c}" "${interfaceCount}")
# Now run the command using the interfaces saved into the array # Now run the command using the interfaces saved into the array
chooseInterfaceOptions=$("${chooseInterfaceCmd[@]}" "${interfacesArray[@]}" 2>&1 >/dev/tty) || \ chooseInterfaceOptions=$("${chooseInterfaceCmd[@]}" "${interfacesArray[@]}" 2>&1 >/dev/tty) || \
# If the user chooses Cancel, exit # If the user chooses Cancel, exit
@ -755,8 +818,8 @@ use4andor6() {
# Named local variables # Named local variables
local useIPv4 local useIPv4
local useIPv6 local useIPv6
# Let use select IPv4 and/or IPv6 via a checklist # Let user choose IPv4 and/or IPv6 via a checklist
cmd=(whiptail --separate-output --checklist "Select Protocols (press space to select)" "${r}" "${c}" 2) cmd=(whiptail --separate-output --checklist "Select Protocols (press space to toggle selection)" "${r}" "${c}" 2)
# In an array, show the options available: # In an array, show the options available:
# IPv4 (on by default) # IPv4 (on by default)
options=(IPv4 "Block ads over IPv4" on options=(IPv4 "Block ads over IPv4" on
@ -819,13 +882,13 @@ It is also possible to use a DHCP reservation, but if you are going to do that,
# Ask for the IPv4 address # Ask for the IPv4 address
IPV4_ADDRESS=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 address" --inputbox "Enter your desired IPv4 address" "${r}" "${c}" "${IPV4_ADDRESS}" 3>&1 1>&2 2>&3) || \ IPV4_ADDRESS=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 address" --inputbox "Enter your desired IPv4 address" "${r}" "${c}" "${IPV4_ADDRESS}" 3>&1 1>&2 2>&3) || \
# Cancelling IPv4 settings window # Canceling IPv4 settings window
{ ipSettingsCorrect=False; echo -e " ${COL_LIGHT_RED}Cancel was selected, exiting installer${COL_NC}"; exit 1; } { ipSettingsCorrect=False; echo -e " ${COL_LIGHT_RED}Cancel was selected, exiting installer${COL_NC}"; exit 1; }
printf " %b Your static IPv4 address: %s\\n" "${INFO}" "${IPV4_ADDRESS}" printf " %b Your static IPv4 address: %s\\n" "${INFO}" "${IPV4_ADDRESS}"
# Ask for the gateway # Ask for the gateway
IPv4gw=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 gateway (router)" --inputbox "Enter your desired IPv4 default gateway" "${r}" "${c}" "${IPv4gw}" 3>&1 1>&2 2>&3) || \ IPv4gw=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 gateway (router)" --inputbox "Enter your desired IPv4 default gateway" "${r}" "${c}" "${IPv4gw}" 3>&1 1>&2 2>&3) || \
# Cancelling gateway settings window # Canceling gateway settings window
{ ipSettingsCorrect=False; echo -e " ${COL_LIGHT_RED}Cancel was selected, exiting installer${COL_NC}"; exit 1; } { ipSettingsCorrect=False; echo -e " ${COL_LIGHT_RED}Cancel was selected, exiting installer${COL_NC}"; exit 1; }
printf " %b Your static IPv4 gateway: %s\\n" "${INFO}" "${IPv4gw}" printf " %b Your static IPv4 gateway: %s\\n" "${INFO}" "${IPv4gw}"
@ -855,7 +918,7 @@ setDHCPCD() {
echo "interface ${PIHOLE_INTERFACE} echo "interface ${PIHOLE_INTERFACE}
static ip_address=${IPV4_ADDRESS} static ip_address=${IPV4_ADDRESS}
static routers=${IPv4gw} static routers=${IPv4gw}
static domain_name_servers=127.0.0.1" | tee -a /etc/dhcpcd.conf >/dev/null static domain_name_servers=${PIHOLE_DNS_1} ${PIHOLE_DNS_2}" | tee -a /etc/dhcpcd.conf >/dev/null
# Then use the ip command to immediately set the new address # Then use the ip command to immediately set the new address
ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}"
# Also give a warning that the user may need to reboot their system # Also give a warning that the user may need to reboot their system
@ -954,22 +1017,34 @@ valid_ip() {
local ip=${1} local ip=${1}
local stat=1 local stat=1
# If the IP matches the format xxx.xxx.xxx.xxx, # One IPv4 element is 8bit: 0 - 256
if [[ "${ip}" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then local ipv4elem="(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)";
# Save the old Internal Field Separator in a variable # optional port number starting '#' with range of 1-65536
OIFS=$IFS local portelem="(#([1-9]|[1-8][0-9]|9[0-9]|[1-8][0-9]{2}|9[0-8][0-9]|99[0-9]|[1-8][0-9]{3}|9[0-8][0-9]{2}|99[0-8][0-9]|999[0-9]|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-6]))?"
# and set the new one to a dot (period) # build a full regex string from the above parts
IFS='.' local regex="^${ipv4elem}\.${ipv4elem}\.${ipv4elem}\.${ipv4elem}${portelem}$"
# Put the IP into an array
read -r -a ip <<< "${ip}" [[ $ip =~ ${regex} ]]
# Restore the IFS to what it was
IFS=${OIFS} stat=$?
## Evaluate each octet by checking if it's less than or equal to 255 (the max for each octet) # Return the exit code
[[ "${ip[0]}" -le 255 && "${ip[1]}" -le 255 \ return "${stat}"
&& "${ip[2]}" -le 255 && "${ip[3]}" -le 255 ]] }
# Save the exit code
valid_ip6() {
local ip=${1}
local stat=1
# One IPv6 element is 16bit: 0000 - FFFF
local ipv6elem="[0-9a-fA-F]{1,4}"
# CIDR for IPv6 is 1- 128 bit
local v6cidr="(\\/([1-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8])){0,1}"
# build a full regex string from the above parts
local regex="^(((${ipv6elem}))((:${ipv6elem}))*::((${ipv6elem}))*((:${ipv6elem}))*|((${ipv6elem}))((:${ipv6elem})){7})${v6cidr}$"
[[ ${ip} =~ ${regex} ]]
stat=$? stat=$?
fi
# Return the exit code # Return the exit code
return "${stat}" return "${stat}"
} }
@ -1206,22 +1281,21 @@ chooseBlocklists() {
mv "${adlistFile}" "${adlistFile}.old" mv "${adlistFile}" "${adlistFile}.old"
fi fi
# Let user select (or not) blocklists via a checklist # Let user select (or not) blocklists via a checklist
cmd=(whiptail --separate-output --checklist "Pi-hole relies on third party lists in order to block ads.\\n\\nYou can use the suggestions below, and/or add your own after installation\\n\\nTo deselect any list, use the arrow keys and spacebar" "${r}" "${c}" 6) cmd=(whiptail --separate-output --checklist "Pi-hole relies on third party lists in order to block ads.\\n\\nYou can use the suggestions below, and/or add your own after installation\\n\\nTo deselect any list, use the arrow keys and spacebar" "${r}" "${c}" 5)
# In an array, show the options available (all off by default): # In an array, show the options available (all off by default):
options=(StevenBlack "StevenBlack's Unified Hosts List" on options=(StevenBlack "StevenBlack's Unified Hosts List" on
MalwareDom "MalwareDomains" on MalwareDom "MalwareDomains" on)
Cameleon "Cameleon" on
DisconTrack "Disconnect.me Tracking" on
DisconAd "Disconnect.me Ads" on
HostsFile "Hosts-file.net Ads" on)
# In a variable, show the choices available; exit if Cancel is selected # In a variable, show the choices available; exit if Cancel is selected
choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty) || { printf " %bCancel was selected, exiting installer%b\\n" "${COL_LIGHT_RED}" "${COL_NC}"; rm "${adlistFile}" ;exit 1; } choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty) || { printf " %bCancel was selected, exiting installer%b\\n" "${COL_LIGHT_RED}" "${COL_NC}"; rm "${adlistFile}" ;exit 1; }
# For each choice available, # create empty adlist file if no list was selected
: > "${adlistFile}"
# For each choice available
for choice in ${choices} for choice in ${choices}
do do
appendToListsFile "${choice}" appendToListsFile "${choice}"
done done
touch "${adlistFile}"
chmod 644 "${adlistFile}" chmod 644 "${adlistFile}"
} }
@ -1232,10 +1306,6 @@ appendToListsFile() {
case $1 in case $1 in
StevenBlack ) echo "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts" >> "${adlistFile}";; StevenBlack ) echo "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts" >> "${adlistFile}";;
MalwareDom ) echo "https://mirror1.malwaredomains.com/files/justdomains" >> "${adlistFile}";; MalwareDom ) echo "https://mirror1.malwaredomains.com/files/justdomains" >> "${adlistFile}";;
Cameleon ) echo "https://sysctl.org/cameleon/hosts" >> "${adlistFile}";;
DisconTrack ) echo "https://s3.amazonaws.com/lists.disconnect.me/simple_tracking.txt" >> "${adlistFile}";;
DisconAd ) echo "https://s3.amazonaws.com/lists.disconnect.me/simple_ad.txt" >> "${adlistFile}";;
HostsFile ) echo "https://hosts-file.net/ad_servers.txt" >> "${adlistFile}";;
esac esac
} }
@ -1249,10 +1319,8 @@ installDefaultBlocklists() {
fi fi
appendToListsFile StevenBlack appendToListsFile StevenBlack
appendToListsFile MalwareDom appendToListsFile MalwareDom
appendToListsFile Cameleon
appendToListsFile DisconTrack appendToListsFile DisconTrack
appendToListsFile DisconAd appendToListsFile DisconAd
appendToListsFile HostsFile
} }
# Check if /etc/dnsmasq.conf is from pi-hole. If so replace with an original and install new in .d directory # Check if /etc/dnsmasq.conf is from pi-hole. If so replace with an original and install new in .d directory
@ -1429,8 +1497,8 @@ installConfigs() {
sed -i 's/^\(server\.error-handler-404\s*=\s*\).*$/\1"pihole\/custom\.php"/' /etc/lighttpd/lighttpd.conf sed -i 's/^\(server\.error-handler-404\s*=\s*\).*$/\1"pihole\/custom\.php"/' /etc/lighttpd/lighttpd.conf
fi fi
# Make the directories if they do not exist and set the owners # Make the directories if they do not exist and set the owners
mkdir -p /var/run/lighttpd mkdir -p /run/lighttpd
chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/run/lighttpd chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /run/lighttpd
mkdir -p /var/cache/lighttpd/compress mkdir -p /var/cache/lighttpd/compress
chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/compress chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/compress
mkdir -p /var/cache/lighttpd/uploads mkdir -p /var/cache/lighttpd/uploads
@ -1770,59 +1838,51 @@ create_pihole_user() {
printf " %b %s..." "${INFO}" "${str}" printf " %b %s..." "${INFO}" "${str}"
# If the user pihole exists, # If the user pihole exists,
if id -u pihole &> /dev/null; then if id -u pihole &> /dev/null; then
# if group exists
if getent group pihole > /dev/null 2>&1; then
# just show a success # just show a success
printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}" printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}"
else
local str="Checking for group 'pihole'"
printf " %b %s..." "${INFO}" "${str}"
local str="Creating group 'pihole'"
# if group can be created
if groupadd pihole; then
printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}"
local str="Adding user 'pihole' to group 'pihole'"
printf " %b %s..." "${INFO}" "${str}"
# if pihole user can be added to group pihole
if usermod -g pihole pihole; then
printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}"
else
printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}"
fi
else
printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}"
fi
fi
# Otherwise, # Otherwise,
else else
printf "%b %b %s" "${OVER}" "${CROSS}" "${str}" printf "%b %b %s" "${OVER}" "${CROSS}" "${str}"
local str="Creating user 'pihole'" local str="Creating user 'pihole'"
printf "%b %b %s..." "${OVER}" "${INFO}" "${str}" printf "%b %b %s..." "${OVER}" "${INFO}" "${str}"
# create her with the useradd command # create her with the useradd command
if getent group pihole > /dev/null 2>&1; then
# add primary group pihole as it already exists
if useradd -r --no-user-group -g pihole -s /usr/sbin/nologin pihole; then
printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}"
else
printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}"
fi
else
# add user pihole with default group settings
if useradd -r -s /usr/sbin/nologin pihole; then if useradd -r -s /usr/sbin/nologin pihole; then
printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}" printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}"
else else
printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}" printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}"
fi fi
fi fi
}
# Allow HTTP and DNS traffic
configureFirewall() {
printf "\\n"
# If a firewall is running,
if firewall-cmd --state &> /dev/null; then
# ask if the user wants to install Pi-hole's default firewall rules
whiptail --title "Firewall in use" --yesno "We have detected a running firewall\\n\\nPi-hole currently requires HTTP and DNS port access.\\n\\n\\n\\nInstall Pi-hole default firewall rules?" "${r}" "${c}" || \
{ printf " %b Not installing firewall rulesets.\\n" "${INFO}"; return 0; }
printf " %b Configuring FirewallD for httpd and pihole-FTL\\n" "${TICK}"
# Allow HTTP and DNS traffic
firewall-cmd --permanent --add-service=http --add-service=dns
# Reload the firewall to apply these changes
firewall-cmd --reload
return 0
# Check for proper kernel modules to prevent failure
elif modinfo ip_tables &> /dev/null && is_command iptables ; then
# If chain Policy is not ACCEPT or last Rule is not ACCEPT
# then check and insert our Rules above the DROP/REJECT Rule.
if iptables -S INPUT | head -n1 | grep -qv '^-P.*ACCEPT$' || iptables -S INPUT | tail -n1 | grep -qv '^-\(A\|P\).*ACCEPT$'; then
whiptail --title "Firewall in use" --yesno "We have detected a running firewall\\n\\nPi-hole currently requires HTTP and DNS port access.\\n\\n\\n\\nInstall Pi-hole default firewall rules?" "${r}" "${c}" || \
{ printf " %b Not installing firewall rulesets.\\n" "${INFO}"; return 0; }
printf " %b Installing new IPTables firewall rulesets\\n" "${TICK}"
# Check chain first, otherwise a new rule will duplicate old ones
iptables -C INPUT -p tcp -m tcp --dport 80 -j ACCEPT &> /dev/null || iptables -I INPUT 1 -p tcp -m tcp --dport 80 -j ACCEPT
iptables -C INPUT -p tcp -m tcp --dport 53 -j ACCEPT &> /dev/null || iptables -I INPUT 1 -p tcp -m tcp --dport 53 -j ACCEPT
iptables -C INPUT -p udp -m udp --dport 53 -j ACCEPT &> /dev/null || iptables -I INPUT 1 -p udp -m udp --dport 53 -j ACCEPT
iptables -C INPUT -p tcp -m tcp --dport 4711:4720 -i lo -j ACCEPT &> /dev/null || iptables -I INPUT 1 -p tcp -m tcp --dport 4711:4720 -i lo -j ACCEPT
return 0
fi fi
# Otherwise,
else
# no firewall is running
printf " %b No active firewall detected.. skipping firewall configuration\\n" "${INFO}"
# so just exit
return 0
fi
printf " %b Skipping firewall configuration\\n" "${INFO}"
} }
# #
@ -1933,8 +1993,6 @@ installPihole() {
# Repair permissions if /var/www/html is not world readable # Repair permissions if /var/www/html is not world readable
chmod a+rx /var/www chmod a+rx /var/www
chmod a+rx /var/www/html chmod a+rx /var/www/html
# Give pihole access to the Web server group
usermod -a -G ${LIGHTTPD_GROUP} pihole
# Give lighttpd access to the pihole group so the web interface can # Give lighttpd access to the pihole group so the web interface can
# manage the gravity.db database # manage the gravity.db database
usermod -a -G pihole ${LIGHTTPD_USER} usermod -a -G pihole ${LIGHTTPD_USER}
@ -1975,10 +2033,6 @@ installPihole() {
# Check if dnsmasq is present. If so, disable it and back up any possible # Check if dnsmasq is present. If so, disable it and back up any possible
# config file # config file
disable_dnsmasq disable_dnsmasq
# Configure the firewall
if [[ "${useUpdateVars}" == false ]]; then
configureFirewall
fi
# install a man page entry for pihole # install a man page entry for pihole
install_manpage install_manpage
@ -1992,9 +2046,9 @@ checkSelinux() {
local DEFAULT_SELINUX local DEFAULT_SELINUX
local CURRENT_SELINUX local CURRENT_SELINUX
local SELINUX_ENFORCING=0 local SELINUX_ENFORCING=0
# Check if a SELinux configuration file exists # Check for SELinux configuration file and getenforce command
if [[ -f /etc/selinux/config ]]; then if [[ -f /etc/selinux/config ]] && command -v getenforce &> /dev/null; then
# If a SELinux configuration file was found, check the default SELinux mode. # Check the default SELinux mode
DEFAULT_SELINUX=$(awk -F= '/^SELINUX=/ {print $2}' /etc/selinux/config) DEFAULT_SELINUX=$(awk -F= '/^SELINUX=/ {print $2}' /etc/selinux/config)
case "${DEFAULT_SELINUX,,}" in case "${DEFAULT_SELINUX,,}" in
enforcing) enforcing)
@ -2359,11 +2413,11 @@ get_binary_name() {
if [[ -f "/.dockerenv" ]]; then if [[ -f "/.dockerenv" ]]; then
printf "%b %b Detected ARM architecture in docker\\n" "${OVER}" "${TICK}" printf "%b %b Detected ARM architecture in docker\\n" "${OVER}" "${TICK}"
# set the binary to be used # set the binary to be used
binary="pihole-FTL-armel-native" l_binary="pihole-FTL-armel-native"
else else
printf "%b %b Detected ARM architecture\\n" "${OVER}" "${TICK}" printf "%b %b Detected ARM architecture\\n" "${OVER}" "${TICK}"
# set the binary to be used # set the binary to be used
binary="pihole-FTL-arm-linux-gnueabi" l_binary="pihole-FTL-arm-linux-gnueabi"
fi fi
fi fi
elif [[ "${machine}" == "x86_64" ]]; then elif [[ "${machine}" == "x86_64" ]]; then
@ -2585,15 +2639,15 @@ main() {
verifyFreeDiskSpace verifyFreeDiskSpace
fi fi
# Update package cache
update_package_cache || exit 1
# Notify user of package availability # Notify user of package availability
notify_package_updates_available notify_package_updates_available
# Install packages used by this installation script # Install packages used by this installation script
install_dependent_packages "${INSTALLER_DEPS[@]}" install_dependent_packages "${INSTALLER_DEPS[@]}"
# Check that the installed OS is officially supported - display warning if not
os_check
# Check if SELinux is Enforcing # Check if SELinux is Enforcing
checkSelinux checkSelinux

View file

@ -14,8 +14,8 @@ while true; do
read -rp " ${QST} Are you sure you would like to remove ${COL_WHITE}Pi-hole${COL_NC}? [y/N] " yn read -rp " ${QST} Are you sure you would like to remove ${COL_WHITE}Pi-hole${COL_NC}? [y/N] " yn
case ${yn} in case ${yn} in
[Yy]* ) break;; [Yy]* ) break;;
[Nn]* ) echo -e "${OVER} ${COL_LIGHT_GREEN}Uninstall has been cancelled${COL_NC}"; exit 0;; [Nn]* ) echo -e "${OVER} ${COL_LIGHT_GREEN}Uninstall has been canceled${COL_NC}"; exit 0;;
* ) echo -e "${OVER} ${COL_LIGHT_GREEN}Uninstall has been cancelled${COL_NC}"; exit 0;; * ) echo -e "${OVER} ${COL_LIGHT_GREEN}Uninstall has been canceled${COL_NC}"; exit 0;;
esac esac
done done
@ -52,7 +52,7 @@ if [[ "${INSTALL_WEB_SERVER}" == true ]]; then
DEPS+=("${PIHOLE_WEB_DEPS[@]}") DEPS+=("${PIHOLE_WEB_DEPS[@]}")
fi fi
# Compatability # Compatibility
if [ -x "$(command -v apt-get)" ]; then if [ -x "$(command -v apt-get)" ]; then
# Debian Family # Debian Family
PKG_REMOVE=("${PKG_MANAGER}" -y remove --purge) PKG_REMOVE=("${PKG_MANAGER}" -y remove --purge)
@ -188,9 +188,17 @@ removeNoPurge() {
echo -e " ${CROSS} Unable to remove 'pihole' user" echo -e " ${CROSS} Unable to remove 'pihole' user"
fi fi
fi fi
# If the pihole group exists, then remove
if getent group "pihole" &> /dev/null; then
if ${SUDO} groupdel pihole 2> /dev/null; then
echo -e " ${TICK} Removed 'pihole' group"
else
echo -e " ${CROSS} Unable to remove 'pihole' group"
fi
fi
echo -e "\\n We're sorry to see you go, but thanks for checking out Pi-hole! echo -e "\\n We're sorry to see you go, but thanks for checking out Pi-hole!
If you need help, reach out to us on Github, Discourse, Reddit or Twitter If you need help, reach out to us on GitHub, Discourse, Reddit or Twitter
Reinstall at any time: ${COL_WHITE}curl -sSL https://install.pi-hole.net | bash${COL_NC} Reinstall at any time: ${COL_WHITE}curl -sSL https://install.pi-hole.net | bash${COL_NC}
${COL_LIGHT_RED}Please reset the DNS on your router/clients to restore internet connectivity ${COL_LIGHT_RED}Please reset the DNS on your router/clients to restore internet connectivity

View file

@ -1,43 +0,0 @@
# Pi-hole: A black hole for Internet advertisements
# (c) 2015, 2016 by Jacob Salmela
# Network-wide ad blocking via your Raspberry Pi
# http://pi-hole.net
# Lighttpd config file for Pi-hole
#
# Pi-hole is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
server.modules = (
"mod_access",
"mod_alias",
"mod_compress",
"mod_redirect",
"mod_rewrite"
)
server.document-root = "/var/www"
server.upload-dirs = ( "/var/cache/lighttpd/uploads" )
server.errorlog = "/var/log/lighttpd/error.log"
server.pid-file = "/var/run/lighttpd.pid"
server.username = "www-data"
server.groupname = "www-data"
server.port = 80
index-file.names = ( "index.php", "index.html", "index.lighttpd.html" )
url.access-deny = ( "~", ".inc" )
static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" )
compress.cache-dir = "/var/cache/lighttpd/compress/"
compress.filetype = ( "application/javascript", "text/css", "text/html", "text/plain" )
# default listening port for IPv6 falls back to the IPv4 port
include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port
include_shell "/usr/share/lighttpd/create-mime.assign.pl"
include_shell "/usr/share/lighttpd/include-conf-enabled.pl"
$HTTP["host"] =~ "ads.hulu.com|ads-v-darwin.hulu.com|ads-e-darwin.hulu.com" {
url.redirect = ( ".*" => "http://192.168.1.101:8200/MediaItems/19.mov")
}

View file

@ -1,17 +0,0 @@
# Pi-hole: A black hole for Internet advertisements
# (c) 2015, 2016 by Jacob Salmela
# Network-wide ad blocking via your Raspberry Pi
# http://pi-hole.net
# MiniDLNA config file for Pi-hole
#
# Pi-hole is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
media_dir=V,/var/lib/minidlna/videos/
port=8200
friendly_name=pihole
serial=12345678
model_number=1
inotify=yes

View file

@ -271,7 +271,7 @@ gravity_CheckDNSResolutionAvailable() {
fi fi
# If the /etc/resolv.conf contains resolvers other than 127.0.0.1 then the local dnsmasq will not be queried and pi.hole is NXDOMAIN. # If the /etc/resolv.conf contains resolvers other than 127.0.0.1 then the local dnsmasq will not be queried and pi.hole is NXDOMAIN.
# This means that even though name resolution is working, the getent hosts check fails and the holddown timer keeps ticking and eventualy fails # This means that even though name resolution is working, the getent hosts check fails and the holddown timer keeps ticking and eventually fails
# So we check the output of the last command and if it failed, attempt to use dig +short as a fallback # So we check the output of the last command and if it failed, attempt to use dig +short as a fallback
if timeout 4 dig +short "${lookupDomain}" &> /dev/null; then if timeout 4 dig +short "${lookupDomain}" &> /dev/null; then
if [[ -n "${secs:-}" ]]; then if [[ -n "${secs:-}" ]]; then
@ -359,9 +359,10 @@ gravity_DownloadBlocklists() {
for ((i = 0; i < "${#sources[@]}"; i++)); do for ((i = 0; i < "${#sources[@]}"; i++)); do
url="${sources[$i]}" url="${sources[$i]}"
domain="${sourceDomains[$i]}" domain="${sourceDomains[$i]}"
id="${sourceIDs[$i]}"
# Save the file as list.#.domain # Save the file as list.#.domain
saveLocation="${piholeDir}/list.${i}.${domain}.${domainsExtension}" saveLocation="${piholeDir}/list.${id}.${domain}.${domainsExtension}"
activeDomains[$i]="${saveLocation}" activeDomains[$i]="${saveLocation}"
# Default user-agent (for Cloudflare's Browser Integrity Check: https://support.cloudflare.com/hc/en-us/articles/200170086-What-does-the-Browser-Integrity-Check-do-) # Default user-agent (for Cloudflare's Browser Integrity Check: https://support.cloudflare.com/hc/en-us/articles/200170086-What-does-the-Browser-Integrity-Check-do-)
@ -374,7 +375,14 @@ gravity_DownloadBlocklists() {
esac esac
echo -e " ${INFO} Target: ${url}" echo -e " ${INFO} Target: ${url}"
local regex
# Check for characters NOT allowed in URLs
regex="[^a-zA-Z0-9:/?&%=~._()-;]"
if [[ "${url}" =~ ${regex} ]]; then
echo -e " ${CROSS} Invalid Target"
else
gravity_DownloadBlocklistFromUrl "${url}" "${cmd_ext}" "${agent}" "${sourceIDs[$i]}" "${saveLocation}" "${target}" gravity_DownloadBlocklistFromUrl "${url}" "${cmd_ext}" "${agent}" "${sourceIDs[$i]}" "${saveLocation}" "${target}"
fi
echo "" echo ""
done done
@ -561,17 +569,19 @@ gravity_ParseFileIntoDomains() {
# Determine if we are parsing a consolidated list # Determine if we are parsing a consolidated list
#if [[ "${source}" == "${piholeDir}/${matterAndLight}" ]]; then #if [[ "${source}" == "${piholeDir}/${matterAndLight}" ]]; then
# Remove comments and print only the domain name # Remove comments and print only the domain name
# Most of the lists downloaded are already in hosts file format but the spacing/formating is not contigious # Most of the lists downloaded are already in hosts file format but the spacing/formating is not contiguous
# This helps with that and makes it easier to read # This helps with that and makes it easier to read
# It also helps with debugging so each stage of the script can be researched more in depth # It also helps with debugging so each stage of the script can be researched more in depth
# 1) Remove carriage returns # 1) Remove carriage returns
# 2) Convert all characters to lowercase # 2) Convert all characters to lowercase
# 3) Remove lines containing "#" or "/" # 3) Remove comments (text starting with "#", include possible spaces before the hash sign)
# 4) Remove leading tabs, spaces, etc. # 4) Remove lines containing "/"
# 5) Delete lines not matching domain names # 5) Remove leading tabs, spaces, etc.
# 6) Delete lines not matching domain names
< "${source}" tr -d '\r' | \ < "${source}" tr -d '\r' | \
tr '[:upper:]' '[:lower:]' | \ tr '[:upper:]' '[:lower:]' | \
sed -r '/(\/|#).*$/d' | \ sed 's/\s*#.*//g' | \
sed -r '/(\/).*$/d' | \
sed -r 's/^.*\s+//g' | \ sed -r 's/^.*\s+//g' | \
sed -r '/([^\.]+\.)+[^\.]{2,}/!d' > "${destination}" sed -r '/([^\.]+\.)+[^\.]{2,}/!d' > "${destination}"
chmod 644 "${destination}" chmod 644 "${destination}"
@ -631,7 +641,7 @@ gravity_Table_Count() {
if [[ "${table}" == "vw_gravity" ]]; then if [[ "${table}" == "vw_gravity" ]]; then
local unique local unique
unique="$(sqlite3 "${gravityDBfile}" "SELECT COUNT(DISTINCT domain) FROM ${table};")" unique="$(sqlite3 "${gravityDBfile}" "SELECT COUNT(DISTINCT domain) FROM ${table};")"
echo -e " ${INFO} Number of ${str}: ${num} (${unique} unique domains)" echo -e " ${INFO} Number of ${str}: ${num} (${COL_BOLD}${unique} unique domains${COL_NC})"
sqlite3 "${gravityDBfile}" "INSERT OR REPLACE INTO info (property,value) VALUES ('gravity_count',${unique});" sqlite3 "${gravityDBfile}" "INSERT OR REPLACE INTO info (property,value) VALUES ('gravity_count',${unique});"
else else
echo -e " ${INFO} Number of ${str}: ${num}" echo -e " ${INFO} Number of ${str}: ${num}"
@ -742,7 +752,7 @@ gravity_Cleanup() {
dnsWasOffline=true dnsWasOffline=true
fi fi
# Print Pi-hole status if an error occured # Print Pi-hole status if an error occurred
if [[ -n "${error}" ]]; then if [[ -n "${error}" ]]; then
"${PIHOLE_COMMAND}" status "${PIHOLE_COMMAND}" status
exit 1 exit 1

View file

@ -1,4 +1,4 @@
.TH "Pi-hole" "8" "Pi-hole" "Pi-hole" "May 2018" .TH "Pi-hole" "8" "Pi-hole" "Pi-hole" "April 2020"
.SH "NAME" .SH "NAME"
Pi-hole : A black-hole for internet advertisements Pi-hole : A black-hole for internet advertisements
@ -11,8 +11,6 @@ Pi-hole : A black-hole for internet advertisements
.br .br
\fBpihole -a\fR (\fB-c|-f|-k\fR) \fBpihole -a\fR (\fB-c|-f|-k\fR)
.br .br
\fBpihole -a\fR [\fB-r\fR hostrecord]
.br
\fBpihole -a -e\fR email \fBpihole -a -e\fR email
.br .br
\fBpihole -a -i\fR interface \fBpihole -a -i\fR interface
@ -43,7 +41,7 @@ pihole -g\fR
.br .br
pihole status pihole status
.br .br
pihole restartdns\fR pihole restartdns\fR [options]
.br .br
\fBpihole\fR (\fBenable\fR|\fBdisable\fR [time]) \fBpihole\fR (\fBenable\fR|\fBdisable\fR [time])
.br .br
@ -134,9 +132,6 @@ Available commands and options:
-f, fahrenheit Set Fahrenheit as preferred temperature unit -f, fahrenheit Set Fahrenheit as preferred temperature unit
.br .br
-k, kelvin Set Kelvin as preferred temperature unit -k, kelvin Set Kelvin as preferred temperature unit
.br
-r, hostrecord Add a name to the DNS associated to an
IPv4/IPv6 address
.br .br
-e, email Set an administrative contact address for the -e, email Set an administrative contact address for the
Block Page Block Page
@ -229,7 +224,7 @@ Available commands and options:
.br .br
-l, --latest Return the latest version -l, --latest Return the latest version
.br .br
--hash Return the Github hash from your local --hash Return the GitHub hash from your local
repositories repositories
.br .br
@ -260,14 +255,21 @@ Available commands and options:
#m Disable Pi-hole functionality for # minute(s) #m Disable Pi-hole functionality for # minute(s)
.br .br
\fBrestartdns\fR \fBrestartdns\fR [options]
.br .br
Restart Pi-hole subsystems Full restart Pi-hole subsystems. Without any options (see below) a full restart causes config file parsing and history re-reading
.br
(restart options):
.br
reload Updates the lists (incl. HOSTS files) and flushes DNS cache. Does not reparse config files
.br
reload-lists Updates the lists (excl. HOSTS files) WITHOUT flushing the DNS cache. Does not reparse config files
.br .br
\fBcheckout\fR [repo] [branch] \fBcheckout\fR [repo] [branch]
.br .br
Switch Pi-hole subsystems to a different Github branch Switch Pi-hole subsystems to a different GitHub branch
.br .br
(repo options): (repo options):

19
pihole
View file

@ -164,7 +164,7 @@ Time:
local str="Disabling blocking for ${tt} seconds" local str="Disabling blocking for ${tt} seconds"
echo -e " ${INFO} ${str}..." echo -e " ${INFO} ${str}..."
local str="Blocking will be re-enabled in ${tt} seconds" local str="Blocking will be re-enabled in ${tt} seconds"
nohup bash -c "sleep ${tt}; ${PI_HOLE_BIN_DIR}/pihole enable" </dev/null &>/dev/null & nohup "${PI_HOLE_SCRIPT_DIR}"/pihole-reenable.sh ${tt} </dev/null &>/dev/null &
else else
local error=true local error=true
fi fi
@ -175,7 +175,7 @@ Time:
echo -e " ${INFO} ${str}..." echo -e " ${INFO} ${str}..."
local str="Blocking will be re-enabled in ${tt} minutes" local str="Blocking will be re-enabled in ${tt} minutes"
tt=$((${tt}*60)) tt=$((${tt}*60))
nohup bash -c "sleep ${tt}; ${PI_HOLE_BIN_DIR}/pihole enable" </dev/null &>/dev/null & nohup "${PI_HOLE_SCRIPT_DIR}"/pihole-reenable.sh ${tt} </dev/null &>/dev/null &
else else
local error=true local error=true
fi fi
@ -197,6 +197,7 @@ Time:
fi fi
else else
# Enable Pi-hole # Enable Pi-hole
killall -q pihole-reenable
if grep -cq "BLOCKING_ENABLED=true" "${setupVars}"; then if grep -cq "BLOCKING_ENABLED=true" "${setupVars}"; then
echo -e " ${INFO} Blocking already enabled, nothing to do" echo -e " ${INFO} Blocking already enabled, nothing to do"
exit 0 exit 0
@ -302,9 +303,9 @@ tailFunc() {
source /etc/pihole/setupVars.conf source /etc/pihole/setupVars.conf
# Strip date from each line # Strip date from each line
# Colour blocklist/blacklist/wildcard entries as red # Color blocklist/blacklist/wildcard entries as red
# Colour A/AAAA/DHCP strings as white # Color A/AAAA/DHCP strings as white
# Colour everything else as gray # Color everything else as gray
tail -f /var/log/pihole.log | sed -E \ tail -f /var/log/pihole.log | sed -E \
-e "s,($(date +'%b %d ')| dnsmasq\[[0-9]*\]),,g" \ -e "s,($(date +'%b %d ')| dnsmasq\[[0-9]*\]),,g" \
-e "s,(.*(blacklisted |gravity blocked ).* is (0.0.0.0|::|NXDOMAIN|${IPV4_ADDRESS%/*}|${IPV6_ADDRESS:-NULL}).*),${COL_RED}&${COL_NC}," \ -e "s,(.*(blacklisted |gravity blocked ).* is (0.0.0.0|::|NXDOMAIN|${IPV4_ADDRESS%/*}|${IPV6_ADDRESS:-NULL}).*),${COL_RED}&${COL_NC}," \
@ -317,7 +318,7 @@ piholeCheckoutFunc() {
if [[ "$2" == "-h" ]] || [[ "$2" == "--help" ]]; then if [[ "$2" == "-h" ]] || [[ "$2" == "--help" ]]; then
echo "Usage: pihole checkout [repo] [branch] echo "Usage: pihole checkout [repo] [branch]
Example: 'pihole checkout master' or 'pihole checkout core dev' Example: 'pihole checkout master' or 'pihole checkout core dev'
Switch Pi-hole subsystems to a different Github branch Switch Pi-hole subsystems to a different GitHub branch
Repositories: Repositories:
core [branch] Change the branch of Pi-hole's core subsystem core [branch] Change the branch of Pi-hole's core subsystem
@ -413,8 +414,10 @@ Options:
enable Enable Pi-hole subsystems enable Enable Pi-hole subsystems
disable Disable Pi-hole subsystems disable Disable Pi-hole subsystems
Add '-h' for more info on disable usage Add '-h' for more info on disable usage
restartdns Restart Pi-hole subsystems restartdns Full restart Pi-hole subsystems
checkout Switch Pi-hole subsystems to a different Github branch Add 'reload' to update the lists and flush the cache without restarting the DNS server
Add 'reload-lists' to only update the lists WITHOUT flushing the cache or restarting the DNS server
checkout Switch Pi-hole subsystems to a different GitHub branch
Add '-h' for more info on checkout usage Add '-h' for more info on checkout usage
arpflush Flush information stored in Pi-hole's network tables"; arpflush Flush information stored in Pi-hole's network tables";
exit 0 exit 0

5
supportedos.txt Normal file
View file

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

View file

@ -7,11 +7,11 @@ From command line all you need to do is:
- `pip install tox` - `pip install tox`
- `tox` - `tox`
Tox handles setting up a virtual environment for python dependancies, installing dependancies, building the docker images used by tests, and finally running tests. It's an easy way to have travis-ci like build behavior locally. Tox handles setting up a virtual environment for python dependencies, installing dependencies, building the docker images used by tests, and finally running tests. It's an easy way to have travis-ci like build behavior locally.
## Alternative py.test method of running tests ## Alternative py.test method of running tests
You're responsible for setting up your virtual env and dependancies in this situation. You're responsible for setting up your virtual env and dependencies in this situation.
``` ```
py.test -vv -n auto -m "build_stage" py.test -vv -n auto -m "build_stage"

View file

@ -14,9 +14,9 @@ SETUPVARS = {
'PIHOLE_DNS_2': '4.2.2.2' 'PIHOLE_DNS_2': '4.2.2.2'
} }
tick_box = "[\x1b[1;32m\xe2\x9c\x93\x1b[0m]".decode("utf-8") tick_box = "[\x1b[1;32m\u2713\x1b[0m]"
cross_box = "[\x1b[1;31m\xe2\x9c\x97\x1b[0m]".decode("utf-8") cross_box = "[\x1b[1;31m\u2717\x1b[0m]"
info_box = "[i]".decode("utf-8") info_box = "[i]"
@pytest.fixture @pytest.fixture
@ -38,9 +38,7 @@ def Pihole(Docker):
return out return out
funcType = type(Docker.run) funcType = type(Docker.run)
Docker.run = funcType(run_bash, Docker.run = funcType(run_bash, Docker)
Docker,
testinfra.backend.docker.DockerBackend)
return Docker return Docker
@ -106,7 +104,7 @@ def mock_command(script, args, container):
#!/bin/bash -e #!/bin/bash -e
echo "\$0 \$@" >> /var/log/{script} echo "\$0 \$@" >> /var/log/{script}
case "\$1" in'''.format(script=script)) case "\$1" in'''.format(script=script))
for k, v in args.iteritems(): for k, v in args.items():
case = dedent(''' case = dedent('''
{arg}) {arg})
echo {res} echo {res}
@ -133,7 +131,7 @@ def mock_command_2(script, args, container):
#!/bin/bash -e #!/bin/bash -e
echo "\$0 \$@" >> /var/log/{script} echo "\$0 \$@" >> /var/log/{script}
case "\$1 \$2" in'''.format(script=script)) case "\$1 \$2" in'''.format(script=script))
for k, v in args.iteritems(): for k, v in args.items():
case = dedent(''' case = dedent('''
\"{arg}\") \"{arg}\")
echo \"{res}\" echo \"{res}\"

View file

@ -18,6 +18,6 @@ run_local = testinfra.get_backend(
def test_build_pihole_image(image, tag): def test_build_pihole_image(image, tag):
build_cmd = run_local('docker build -f {} -t {} .'.format(image, tag)) build_cmd = run_local('docker build -f {} -t {} .'.format(image, tag))
if build_cmd.rc != 0: if build_cmd.rc != 0:
print build_cmd.stdout print(build_cmd.stdout)
print build_cmd.stderr print(build_cmd.stderr)
assert build_cmd.rc == 0 assert build_cmd.rc == 0

View file

@ -1,6 +1,6 @@
from textwrap import dedent from textwrap import dedent
import re import re
from conftest import ( from .conftest import (
SETUPVARS, SETUPVARS,
tick_box, tick_box,
info_box, info_box,
@ -34,7 +34,7 @@ def test_setupVars_are_sourced_to_global_scope(Pihole):
This confirms the sourced variables are in scope between functions This confirms the sourced variables are in scope between functions
''' '''
setup_var_file = 'cat <<EOF> /etc/pihole/setupVars.conf\n' setup_var_file = 'cat <<EOF> /etc/pihole/setupVars.conf\n'
for k, v in SETUPVARS.iteritems(): for k, v in SETUPVARS.items():
setup_var_file += "{}={}\n".format(k, v) setup_var_file += "{}={}\n".format(k, v)
setup_var_file += "EOF\n" setup_var_file += "EOF\n"
Pihole.run(setup_var_file) Pihole.run(setup_var_file)
@ -59,7 +59,7 @@ def test_setupVars_are_sourced_to_global_scope(Pihole):
output = run_script(Pihole, script).stdout output = run_script(Pihole, script).stdout
for k, v in SETUPVARS.iteritems(): for k, v in SETUPVARS.items():
assert "{}={}".format(k, v) in output assert "{}={}".format(k, v) in output
@ -69,7 +69,7 @@ def test_setupVars_saved_to_file(Pihole):
''' '''
# dedent works better with this and padding matching script below # dedent works better with this and padding matching script below
set_setup_vars = '\n' set_setup_vars = '\n'
for k, v in SETUPVARS.iteritems(): for k, v in SETUPVARS.items():
set_setup_vars += " {}={}\n".format(k, v) set_setup_vars += " {}={}\n".format(k, v)
Pihole.run(set_setup_vars).stdout Pihole.run(set_setup_vars).stdout
@ -88,172 +88,10 @@ def test_setupVars_saved_to_file(Pihole):
output = run_script(Pihole, script).stdout output = run_script(Pihole, script).stdout
for k, v in SETUPVARS.iteritems(): for k, v in SETUPVARS.items():
assert "{}={}".format(k, v) in output assert "{}={}".format(k, v) in output
def test_configureFirewall_firewalld_running_no_errors(Pihole):
'''
confirms firewalld rules are applied when firewallD is running
'''
# firewallD returns 'running' as status
mock_command('firewall-cmd', {'*': ('running', 0)}, Pihole)
# Whiptail dialog returns Ok for user prompt
mock_command('whiptail', {'*': ('', 0)}, Pihole)
configureFirewall = Pihole.run('''
source /opt/pihole/basic-install.sh
configureFirewall
''')
expected_stdout = 'Configuring FirewallD for httpd and pihole-FTL'
assert expected_stdout in configureFirewall.stdout
firewall_calls = Pihole.run('cat /var/log/firewall-cmd').stdout
assert 'firewall-cmd --state' in firewall_calls
assert ('firewall-cmd '
'--permanent '
'--add-service=http '
'--add-service=dns') in firewall_calls
assert 'firewall-cmd --reload' in firewall_calls
def test_configureFirewall_firewalld_disabled_no_errors(Pihole):
'''
confirms firewalld rules are not applied when firewallD is not running
'''
# firewallD returns non-running status
mock_command('firewall-cmd', {'*': ('not running', '1')}, Pihole)
configureFirewall = Pihole.run('''
source /opt/pihole/basic-install.sh
configureFirewall
''')
expected_stdout = ('No active firewall detected.. '
'skipping firewall configuration')
assert expected_stdout in configureFirewall.stdout
def test_configureFirewall_firewalld_enabled_declined_no_errors(Pihole):
'''
confirms firewalld rules are not applied when firewallD is running, user
declines ruleset
'''
# firewallD returns running status
mock_command('firewall-cmd', {'*': ('running', 0)}, Pihole)
# Whiptail dialog returns Cancel for user prompt
mock_command('whiptail', {'*': ('', 1)}, Pihole)
configureFirewall = Pihole.run('''
source /opt/pihole/basic-install.sh
configureFirewall
''')
expected_stdout = 'Not installing firewall rulesets.'
assert expected_stdout in configureFirewall.stdout
def test_configureFirewall_no_firewall(Pihole):
''' confirms firewall skipped no daemon is running '''
configureFirewall = Pihole.run('''
source /opt/pihole/basic-install.sh
configureFirewall
''')
expected_stdout = 'No active firewall detected'
assert expected_stdout in configureFirewall.stdout
def test_configureFirewall_IPTables_enabled_declined_no_errors(Pihole):
'''
confirms IPTables rules are not applied when IPTables is running, user
declines ruleset
'''
# iptables command exists
mock_command('iptables', {'*': ('', '0')}, Pihole)
# modinfo returns always true (ip_tables module check)
mock_command('modinfo', {'*': ('', '0')}, Pihole)
# Whiptail dialog returns Cancel for user prompt
mock_command('whiptail', {'*': ('', '1')}, Pihole)
configureFirewall = Pihole.run('''
source /opt/pihole/basic-install.sh
configureFirewall
''')
expected_stdout = 'Not installing firewall rulesets.'
assert expected_stdout in configureFirewall.stdout
def test_configureFirewall_IPTables_enabled_rules_exist_no_errors(Pihole):
'''
confirms IPTables rules are not applied when IPTables is running and rules
exist
'''
# iptables command exists and returns 0 on calls
# (should return 0 on iptables -C)
mock_command('iptables', {'-S': ('-P INPUT DENY', '0')}, Pihole)
# modinfo returns always true (ip_tables module check)
mock_command('modinfo', {'*': ('', '0')}, Pihole)
# Whiptail dialog returns Cancel for user prompt
mock_command('whiptail', {'*': ('', '0')}, Pihole)
configureFirewall = Pihole.run('''
source /opt/pihole/basic-install.sh
configureFirewall
''')
expected_stdout = 'Installing new IPTables firewall rulesets'
assert expected_stdout in configureFirewall.stdout
firewall_calls = Pihole.run('cat /var/log/iptables').stdout
# General call type occurances
assert len(re.findall(r'iptables -S', firewall_calls)) == 1
assert len(re.findall(r'iptables -C', firewall_calls)) == 4
assert len(re.findall(r'iptables -I', firewall_calls)) == 0
# Specific port call occurances
assert len(re.findall(r'tcp --dport 80', firewall_calls)) == 1
assert len(re.findall(r'tcp --dport 53', firewall_calls)) == 1
assert len(re.findall(r'udp --dport 53', firewall_calls)) == 1
assert len(re.findall(r'tcp --dport 4711:4720', firewall_calls)) == 1
def test_configureFirewall_IPTables_enabled_not_exist_no_errors(Pihole):
'''
confirms IPTables rules are applied when IPTables is running and rules do
not exist
'''
# iptables command and returns 0 on calls (should return 1 on iptables -C)
mock_command(
'iptables',
{
'-S': (
'-P INPUT DENY',
'0'
),
'-C': (
'',
1
),
'-I': (
'',
0
)
},
Pihole
)
# modinfo returns always true (ip_tables module check)
mock_command('modinfo', {'*': ('', '0')}, Pihole)
# Whiptail dialog returns Cancel for user prompt
mock_command('whiptail', {'*': ('', '0')}, Pihole)
configureFirewall = Pihole.run('''
source /opt/pihole/basic-install.sh
configureFirewall
''')
expected_stdout = 'Installing new IPTables firewall rulesets'
assert expected_stdout in configureFirewall.stdout
firewall_calls = Pihole.run('cat /var/log/iptables').stdout
# General call type occurances
assert len(re.findall(r'iptables -S', firewall_calls)) == 1
assert len(re.findall(r'iptables -C', firewall_calls)) == 4
assert len(re.findall(r'iptables -I', firewall_calls)) == 4
# Specific port call occurances
assert len(re.findall(r'tcp --dport 80', firewall_calls)) == 2
assert len(re.findall(r'tcp --dport 53', firewall_calls)) == 2
assert len(re.findall(r'udp --dport 53', firewall_calls)) == 2
assert len(re.findall(r'tcp --dport 4711:4720', firewall_calls)) == 2
def test_selinux_not_detected(Pihole): def test_selinux_not_detected(Pihole):
''' '''
confirms installer continues when SELinux configuration file does not exist confirms installer continues when SELinux configuration file does not exist

View file

@ -1,10 +1,9 @@
import pytest import pytest
from conftest import ( from .conftest import (
tick_box, tick_box,
info_box, info_box,
cross_box, cross_box,
mock_command, mock_command,
mock_command_2,
) )

View file

@ -14,5 +14,5 @@ def test_scripts_pass_shellcheck():
"shellcheck -x \"$file\" -e SC1090,SC1091; " "shellcheck -x \"$file\" -e SC1090,SC1091; "
"done;") "done;")
results = run_local(shellcheck) results = run_local(shellcheck)
print results.stdout print(results.stdout)
assert '' == results.stdout assert '' == results.stdout

View file

@ -1,5 +1,5 @@
[tox] [tox]
envlist = py27 envlist = py36
[testenv] [testenv]
whitelist_externals = docker whitelist_externals = docker