├── _config.yml ├── LICENSE └── README.md /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-midnight 2 | readme_index: 3 | enabled: true 4 | remove_originals: false -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Nathaniel Ledford 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Installing **Pi-Hole** and **PiVPN** on a VPS 2 | 3 | [PiVPN has been discontinued.](https://github.com/pivpn/pivpn/blob/84cd315a522d99717cc4f103c5870b8d014bf846/README.md) This guide should still work for now, but I will be updating the guide to use Wireguard instead ASAP. 4 | 5 | ## Table of Contents 6 | 7 | 1. [Introduction](#Introduction) 8 | 2. [Prerequisites](#Prerequisites) 9 | 3. [Initial Server Setup](#Initial-Server-Setup) 10 | * [`root` Login](#root-login) 11 | * [Public Key Authentication](#public-key-authentication) 12 | * [(Optional) Install **Mosh**](#(Optional)-install-mosh) 13 | * [Set Up `ufw`](#set-up-ufw) 14 | 4. [Install Pi-Hole](#Install-Pi-Hole) 15 | * [(Optional) Configure Pi-Hole](#(optional)-configure-pi-hole) 16 | 5. [Install PiVPN](#install-pivpn) 17 | 6. [Configure Pi-Hole and PiVPN](#configure-pi-hole-and-pivpn) 18 | * [`dnsmasq`](#dnsmasq) 19 | * [Network Adjustments](#network-adjustments) 20 | 7. [Let's Encrypt](#lets-encrypt) 21 | * [Acquiring The Certificate](#acquiring-the-certificate) 22 | * [Configure The Redirect](#configure-the-redirect) 23 | 8. [Sources](#sources) 24 | 25 | ## Introduction 26 | 27 | The purpose of this guide is to document the steps I take to set up [Pi-Hole](https://pi-hole.net/) and [PiVPN](http://www.pivpn.io/) on a VPS, from companies such as [DigitalOcean](https://www.digitalocean.com/) or [Vultr](https://www.vultr.com/). The ultimate goal is to have an ad-blocker that will work both on my home network and on any device connected to the VPN. 28 | 29 | Almost every tutorial I found was focused on installing **Pi-Hole** and **PiVPN** on a local Raspberry Pi instead of on a VPS. The steps are mostly the same but there are some extra steps involved in securing the VPS to deny access from bad actors. 30 | 31 | After completing this tutorial, you will have: 32 | 33 | - A **Pi-Hole** accessible from anywhere 34 | - A VPN that will provide an encrypted connection when using public Wi-Fi, via **PiVPN** 35 | 36 | ## Prerequisites 37 | 38 | In order to follow this tutorial you will need to have a VPS with at least 512 MB of memory, although I would personally recommend at least 1 GB if you plan on having a large number of blocklists. This guide assumes that you are using Ubuntu 18.04 and Pi-Hole Version 4.2. Other distros will mostly likely work, but I have only tested the steps covered in this tutorial on Ubuntu 18.04. 39 | 40 | Companies like **DigitalOcean** provide [tutorials](https://www.digitalocean.com/docs/droplets/how-to/create/) for creating a VPS on their servers. 41 | 42 | ## Initial Server Setup 43 | 44 | We will be using `ssh` to remotely log into the VPS and configure it. If you are on a Unix-based operating system, it should already be installed. If you are Windows, you will need to install [PuTTY](http://www.putty.org/). Make sure you know your server's IP address and login credentials. 45 | 46 | This section essentially covers all of the steps from [**DigitalOcean**'s tutorial for setting up Ubuntu](https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-18-04), but with a few differences. We will be creating a specific user, `pi`, that will use for logging into our VPS and handling the `.ovpn` files generated by **PiVPN**. 47 | 48 | ### `root` Login 49 | 50 | When you have your server's IP address and root passphrase, log into the server as the `root` user 51 | 52 | ```shell 53 | ssh root@your_server_ip 54 | ``` 55 | 56 | You will be asked to create a new passphrase. Although we will be disabling password authentication, be sure to create or [generate](http://passwordsgenerator.net/) a secure passphrase anyway. 57 | 58 | Create new user `pi` 59 | 60 | ```shell 61 | adduser pi 62 | ``` 63 | 64 | Grant root privileges to `pi` 65 | 66 | ```shell 67 | usermod -aG sudo pi 68 | ``` 69 | 70 | ### Public Key Authentication 71 | 72 | [Public Key Authentication](https://the.earth.li/~sgtatham/putty/0.55/htmldoc/Chapter8.html) provides an alternative method of identifying yourselve to a remote server and increases the overall security of your server. 73 | 74 | If you do not already have an SSH key, you will need to create one on your local computer 75 | 76 | ```shell 77 | ssh-keygen 78 | ``` 79 | 80 | Save your key in the default file (where `$user` is your user) 81 | 82 | ```shell 83 | Enter file in which to save the key (/Users/$user/.ssh/id_rsa): 84 | ``` 85 | 86 | Create a secure passphrase. You will need to enter this passphrase each time you utilize your SSH key 87 | 88 | Copy the public key from your local machine to your remote server with `ssh-copy-id` 89 | 90 | ```shell 91 | ssh-copy-id pi@your_server_ip 92 | ``` 93 | 94 | - If you opted to add SSH during the server creation process anyway, this method will not work. 95 | 96 | You should repeat these steps for each device you want to access the server, including desktops, laptops, tablets, and mobile phones. 97 | 98 | #### Disable Passphrase Authentication 99 | 100 | Once you have added SSH keys from all of your devices, we can disable passphrase authentication. 101 | 102 | Log into your server as `root`, if you are not already logged in 103 | 104 | ```shell 105 | ssh root@your_server_ip 106 | ``` 107 | 108 | Open the SSH daemon configuration file 109 | 110 | ```shell 111 | sudo nano /etc/ssh/ssdh_config 112 | ``` 113 | 114 | - Find the line containing `PasswordAuthentication` and uncomment it by deleting the preceeding `#`. Change it's value to **no** 115 | - Find the line containing `PubkeyAuthentication` and ensure it's value is set to **yes** 116 | - Find the line containing `ChallengeResponseAuthentication` and ensure it's value is set to **no** 117 | 118 | Save your changes and close the file 119 | 120 | - `CTRL + X` 121 | - `Y` 122 | - `ENTER` 123 | 124 | While still logged in as `root`, open a new terminal window and test logging in as `pi` and verify that the public key authentication works 125 | 126 | ```shell 127 | ssh pi@your_server_ip 128 | ``` 129 | 130 | ### (Optional) Install **Mosh** 131 | 132 | [**Mosh**](https://mosh.org/), or **mo**bile **sh**ell, is a remote terminal application that allows roaming and intermittent connectivity. It's intended as a replacement for SSH but both can be used on the same server. 133 | 134 | ``` 135 | # Update your sources, if necessary 136 | sudo apt update 137 | 138 | # Install mosh 139 | sudo apt install mosh 140 | ``` 141 | 142 | ### Set up **`ufw`** 143 | 144 | We will set up a basic firewall, [`ufw`](https://wiki.debian.org/Uncomplicated%20Firewall%20%28ufw%29), that will restrict access to certain services on the VPS. Specifically, we want to ensure that only ports needed for SSH, **Pi-Hole**, and **PiVPN** are open. Additional ports can be opened later depending on your specific needs. 145 | 146 | We will be opening ports for secure FTP so that `.ovpn` files needed for connecting to our VPN later can be retrieved via a FTP application such as [Filezilla](https://filezilla-project.org/) or [Transmit](https://panic.com/transmit/). 147 | 148 | To set up `ufw`, enter the following commands: 149 | 150 | ```shell 151 | # Apply basic defaults 152 | sudo ufw default deny incoming 153 | sudo ufw default allow outgoing 154 | 155 | # Open ports for OpenSSH 156 | sudo ufw allow OpenSSH 157 | 158 | # Optionally, allow all access from your IP Address 159 | sudo ufw allow from $yourIPAddress 160 | 161 | # Open ports for secure FTP 162 | sudo ufw allow sftp 163 | 164 | # Open ports for Mosh if you installed it 165 | sudo ufw allow mosh 166 | ``` 167 | 168 | ## Install **Pi-Hole** 169 | 170 | Now that our server has been set up and is secure, we will now install the **Pi-Hole** software. The installation is fairly simple and requires a small amount of configuration on our part. 171 | 172 | Please note that on a Raspberry Pi we would be asked to set a [static IP address](https://support.google.com/fiber/answer/3547208?hl=en). This is important because we do not want the IP address of a DNS server to be constantly changing. However, since we are using a VPS, the static IP address has already been set for us. The networking interface will also be automatically selected as well since only one interface, `eth0`, will be available to us at the time of installation. 173 | 174 | Run the offical [**Pi-Hole** installer](https://github.com/pi-hole/pi-hole/blob/master/automated%20install/basic-install.sh): 175 | 176 | ```shell 177 | curl -sSL https://install.pi-hole.net | bash 178 | ``` 179 | 180 | - When asked about which protocols to use for blocking ads, select both `IPv4` and `IPv6`, even if you cannot use `IPv6` yet on your home network. The justification is that more ads are now being served via `IPv6` and we want to ensure all ads are blocked 181 | - On the very last screen, you will be presented various information about your new **Pi-Hole** installation. Your **Pi-Hole**'s IP address should match your server's IP address. 182 | 183 | Once you have completed the **Pi-Hole** installation script, you should change the passphrase to the admin panel: 184 | 185 | ```shell 186 | pihole -a -p myawesomepassphrase 187 | ``` 188 | 189 | ### (Optional) Configure **Pi-Hole** 190 | 191 | **Pi-Hole** allows you to customize what websites you want to block and allows to you whitelist any false positives (e.g., unblocking Netflix or Facebook). **Pi-Hole** developer [WaLLy3K](https://github.com/WaLLy3K) provides a [popular collection of blocklists](https://wally3k.github.io/) that you can add to your own blocklists. Another blocklist collection is provided by [the Block List Project](https://tspprs.com/). 192 | 193 | I would also recommend checking out [this GitHub repository](https://github.com/anudeepND/whitelist) that will load commonly whitelisted domains (e.g., Facebook, Instagram, XBox Live) into your Pi-Hole. 194 | 195 | Finally, I would suggest following [this guide](https://docs.pi-hole.net/guides/unbound/) from the official Pi-Hole documentation to set up [**unbound**](https://nlnetlabs.nl/projects/unbound/about/) as your own recursive DNS server (rather than using a public DNS server such as Google DNS or Cloudflare). This will help to further increase the privacy of your DNS queries. 196 | 197 | ## Install **PiVPN** 198 | 199 | Installing **PiVPN** will be just as easy as installing **Pi-Hole**, although there is a bit more configuration required on our part for **PiVPN**. **PiVPN** automatically installs an [**OpenVPN** server](https://openvpn.net/) for us as well as any additional required software. The script will also automatically open ports in `ufw` so that an **OpenVPN** client can communicate with our VPS. 200 | 201 | Please note that on a Raspberry Pi, we would be asked to select a network interface, but since we are on a VPS the only available interface is `eth0` and that is automatically selected for us as well as the static IP address. 202 | 203 | Start by running the [**PiVPN** installer](https://github.com/pivpn/pivpn/blob/master/auto_install/install.sh) 204 | 205 | ```shell 206 | curl -L https://install.pivpn.io | bash 207 | ``` 208 | 209 | - When asked to choose a local user to hold your `.ovpn` configuration files, select the user `pi` 210 | - When asked about enabling `UnattendedUpgrades`, pick yes 211 | - When asked to select the protocol, pick `UDP` 212 | - When asked to select the port, either accept the default `1194` or enter a random port (e.g., `11948`) 213 | - When asked to set the size of your encryption key, select `2048` 214 | - Generating the encryption key will take a few minutes 215 | - When asked to select a Public IP or DNS, select your server's IP address 216 | - When asked to select a DNS provider, select the `custom` option and enter the IP address of your server 217 | 218 | Once the installer is finished, allow it to reboot your VPS 219 | 220 | ## Configure **Pi-Hole** and **PiVPN** 221 | 222 | Now that both **Pi-Hole** and **PiVPN** are installed, there are a couple of critical steps we must take before we can start generating `.ovpn` configuration files and connecting to our VPS. Specifically we want to ensure that **PiVPN** uses **Pi-Hole** as it's DNS server and that we can connect using an **OpenVPN** client. 223 | 224 | ### `dnsmasq` 225 | 226 | First we will create a configuration file for `dnsmasq`, the DNS service that powers **Pi-Hole** 227 | 228 | Log into your server as `pi` if you are not logged in already: 229 | 230 | ```shell 231 | ssh pi@your_server_ip 232 | ``` 233 | 234 | Create a new configuration file called `02-pivpn.conf`: 235 | 236 | ```shell 237 | sudo nano /etc/02-pivpn.conf 238 | ``` 239 | 240 | Add the following line to the file: 241 | 242 | ```shell 243 | listen-address=127.0.0.1, your_server_ip, 10.8.0.1 244 | ``` 245 | 246 | Save and exit the file, and restart **Pi-Hole**'s FTL service: 247 | 248 | ```shell 249 | pihole restartdns 250 | ``` 251 | 252 | ### Network Adjustments 253 | 254 | Second we will need to adjust rules in `ufw` to allow **OpenVPN** to correctly route connections. This section covers Step 8 from [**DigitalOcean**'s guide to setting up OpenVPN on a VPS](https://www.digitalocean.com/community/tutorials/how-to-set-up-an-openvpn-server-on-ubuntu-16-04#step-8-adjust-the-server-networking-configuration). 255 | 256 | We will start by modifying the `sysctl.conf` file to allow IP forwarding: 257 | 258 | ```shell 259 | sudo nano /etc/sysctl.conf 260 | ``` 261 | 262 | Look for the line that contains `net.ipv4.ip_forward`. If there is a `#` character prepended, remove it to uncomment the line. Ensure that it is set to `1` and not `0`. 263 | 264 | ```shell 265 | net.ipv4.ip_forward=1 266 | ``` 267 | 268 | Save and close the file. Then instruct `sysctl` to reload it. 269 | 270 | ```shell 271 | sudo sysctl -p 272 | ``` 273 | 274 | Then we will modify `ufw` to allow masquerading of client connections. Before we can modify any rules, we need to find the public network interface of our VPS: 275 | 276 | ```shell 277 | ip route | grep default 278 | ``` 279 | 280 | Your public interface will follow the word "`dev`" in the output. For example: 281 | 282 | ```shell 283 | default via 203.0.113.1 dev eth0 proto static metric 600 284 | ``` 285 | 286 | If your public interface is not `eth0`, make note of what it is. We will be using that interface to modify a `ufw` file that loads rules before regular rules are loaded. We will be adding a rule that will masquerade any traffic comming in from the VPN. 287 | 288 | Open the `before.rules` file: 289 | 290 | ```shell 291 | sudo nano /etc/ufw/before.rules 292 | ``` 293 | 294 | Towards the top of `before.rules` add the following text, starting with `# START OPENVPN RULES`: 295 | 296 | ```text 297 | # 298 | # rules.before 299 | # 300 | # Rules that should be run before the ufw command line added rules. Custom 301 | # rules should be added to one of these chains: 302 | # ufw-before-input 303 | # ufw-before-output 304 | # ufw-before-forward 305 | # 306 | 307 | # START OPENVPN RULES 308 | # NAT table rules 309 | *nat 310 | :POSTROUTING ACCEPT [0:0] 311 | # Allow traffic from OpenVPN client to eth0 (change to the interface you discovered!) 312 | -A POSTROUTING -s 10.8.0.0/8 -o eth0 -j MASQUERADE 313 | COMMIT 314 | # END OPENVPN RULES 315 | 316 | ``` 317 | 318 | Save and close the `before.rules`. 319 | 320 | Finally, we need to tell `ufw` to allow forward packets by default. Open the `/etc/default/ufw` file: 321 | 322 | ```shell 323 | sudo nano /etc/default/ufw 324 | ``` 325 | 326 | Fine the line containing `DEFAULT_FORWARD_POLICY`. Change the value to `ACCEPT` if necessary: 327 | 328 | ```text 329 | DEFAULT_FORWARD_POLICY="ACCEPT" 330 | ``` 331 | 332 | Save and close `/etc/default/ufw`. 333 | 334 | Enter the following commands to restart `ufw` and **OpenVPN**: 335 | 336 | ```shell 337 | # Restart ufw 338 | sudo ufw disable 339 | sudo ufw enable 340 | 341 | # Restart OpenVPN 342 | sudo service openvpn reload 343 | ``` 344 | 345 | ## Let's Encrypt 346 | 347 | The following section is optional and **requires you to have your own domain name**, but it will configure your Pi-Hole's web interface to use `https` courtesy of [Let's Encrypt](https://letsencrypt.org/) and [Certbot](https://certbot.eff.org/). It can be considered overkill just for Pi-Hole, but it certainly doesn't hurt. First we will acquire the certificate and then we will configure `lighttdp` to automatically redirect any `http` requests to `https`. The steps are based on [this reddit post](https://www.reddit.com/r/pihole/comments/6e2jyr/self_signed_ssl_cert_for_the_admin_login_page/di7ct0b). 348 | 349 | ### Acquiring the certificate 350 | 351 | 1. Log into your remote server again either as `root` or with root privileges. 352 | 2. Go to this [Certbot page](https://certbot.eff.org/lets-encrypt/ubuntubionic-other) (for Ubuntu 18.04) and following the **Install** commands to install Certbot on your server. 353 | 3. Perform a dry run to acquire a certificate for your domain. For example: 354 | ```bash 355 | certbot certonly --webroot -w /var/www/html -d example.com --dry-run 356 | ``` 357 | 4. If acquiring the certificate was successful, run the same command again without `--dry-run`. For example: 358 | ```bash 359 | certbot certonly --webroot -w /var/www/html -d example.com 360 | ``` 361 | 5. Edit the file `/etc/lighttpd/conf-available/10-ssl.conf`. Replace `example.com` with your own domain name: 362 | ```bash 363 | ssl.pemfile = "/etc/letsencrypt/live/example.com/combined.pem" 364 | ssl.ca-file = "/etc/letsencrypt/live/example.com/chain.pem" 365 | ``` 366 | 6. Run the following commands, replacing `example.com` with your domain name: 367 | ```bash 368 | ln -s /etc/lighttpd/conf-available/10-ssl.conf /etc/lighttpd/conf-enabled/10-ssl.conf 369 | cd /etc/letsencrypt/live/example.com/ 370 | cat privkey.pem cert.pem > combined.pem 371 | ``` 372 | 7. Restart `lighttpd`: 373 | ```bash 374 | sudo systemctl restart lighttpd 375 | ``` 376 | `https` should now be enabled on your web interface. 377 | 8. Add a cron job to automatically renew the certificate every 90 days. Open `/etc/crontab` and add the following line: 378 | 379 | ```bash 380 | 47 5 * * * root certbot renew --quiet --no-self-upgrade --renew-hook "cat $RENEWED_LINEAGE/privkey.pem $RENEWED_LINEAGE/cert.pem > $RENEWED_LINEAGE/combined.pem;systemctl reload-or-try-restart lighttpd" 381 | ``` 382 | 383 | ### Configure the Redirect 384 | 385 | Open the `lighttpd` configuration file, `/etc/lighttpd/lighttpd.conf`, and add the following block of code: 386 | 387 | ```bash 388 | compress.cache-dir = "/var/cache/lighttpd/compress/" 389 | compress.filetype = ( "application/javascript", "text/css", "text/html", "text/plain" ) 390 | 391 | # [add after the syntax above] 392 | 393 | # Redirect HTTP to HTTPS 394 | $HTTP["scheme"] == "http" { 395 | $HTTP["host"] =~ ".*" { 396 | url.redirect = (".*" => "https://%0$0") 397 | } 398 | } 399 | ``` 400 | 401 | Restart `lighttpd` again: 402 | 403 | ```bash 404 | sudo systemctl restart lighttpd 405 | ``` 406 | 407 | Your web interface should now automatically redirect any `http` requests to `https`. 408 | 409 | 410 | ## Sources 411 | 412 | - **Pi-Hole** 413 | - [Official Website](https://pi-hole.net/) 414 | - [Github](https://github.com/pi-hole/pi-hole) 415 | - **PiVPN** 416 | - [Official Website](http://www.pivpn.io/) 417 | - [Github](https://github.com/pivpn/pivpn) 418 | - [Digital Ocean: Initial Server Setup with Ubuntu 16.04](https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-16-04) 419 | - [Secure Password Generator](http://passwordsgenerator.net/) 420 | - [Using public keys for SSH authentication](https://the.earth.li/~sgtatham/putty/0.55/htmldoc/Chapter8.html) 421 | - [Mosh](https://mosh.org/) 422 | - [Debian Wiki: Uncomplicated Firewall](https://wiki.debian.org/Uncomplicated%20Firewall%20%28ufw%29) 423 | - [FileZilla](https://filezilla-project.org/) 424 | - [Transmit](https://panic.com/transmit/) 425 | - [Static vs. dynamic IP addresses](https://support.google.com/fiber/answer/3547208?hl=en) 426 | - [The Big Blocklist Collection](https://wally3k.github.io/) 427 | - [Pi-Hole: Commonly Whitelisted Domains](https://discourse.pi-hole.net/t/commonly-whitelisted-domains/212) 428 | - [OpenVPN](https://openvpn.net/) 429 | - [How To Set Up an OpenVPN Server on Ubuntu 16.04](https://www.digitalocean.com/community/tutorials/how-to-set-up-an-openvpn-server-on-ubuntu-16-04#step-8-adjust-the-server-networking-configuration) 430 | - 431 | --------------------------------------------------------------------------------