First enable swap just incase
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
sudo cp /etc/fstab /etc/fstab.bak
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
sudo sysctl vm.swappiness=10
sudo sysctl vm.vfs_cache_pressure=50
Also add it to:
nano /etc/sysctl.conf
vm.swappiness = 10
vm.vfs_cache_pressure = 50
apt-get update
apt-get -y install software-properties-common build-essential dialog rsyslog apt-utils
#sudo LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php
#apt-get update
apt-get -y full-upgrade
dpkg-reconfigure tzdata
apt-get install -y curl net-tools make wget php-fpm php-sqlite3 php-zip git man-db nano iptables-persistent nginx dnsutils python3-certbot-nginx libevent-dev libssl-dev libexpat1-dev cron python-pyinotify
Get it from here: https://gist.github.com/bruvv/4b1283fa7902447b3d2ae69481ea8ffa
mkdir ~/build-unbound
cd ~/build-unbound
wget http://www.unbound.net/downloads/unbound-latest.tar.gz
tar -xvf unbound*
cd unbound-1.*/
useradd -r unbound
./configure \
--with-pthreads \
--with-username=unbound \
--with-ssl \
--with-libevent \
--enable-tfo-server \
--enable-tfo-client \
--enable-event-api \
--enable-subnet
make && make install
ln -s /usr/local/etc/unbound/ /etc/unbound
ldconfig
mkdir /usr/local/etc/unbound/trust
#mkdir -p /etc/unbound/unbound.conf.d
chown unbound /usr/local/etc/unbound/trust
sudo -u unbound unbound-control-setup -d /usr/local/etc/unbound/trust/
sudo -u unbound unbound-anchor -a /usr/local/etc/unbound/trust/root.key
wget -O /etc/unbound/trust/root.hints https://www.internic.net/domain/named.root
cat << EOM > /lib/systemd/system/unbound.service
[Unit]
Description=Unbound DNS server
Documentation=man:unbound(8)
After=network.target
Before=nss-lookup.target
Wants=nss-lookup.target
[Service]
Type=simple
Restart=on-failure
EnvironmentFile=-/etc/default/unbound
ExecStartPre=/usr/local/sbin/unbound-anchor -a /usr/local/etc/unbound/trust/root.key
ExecStart=/usr/local/sbin/unbound -c /usr/local/etc/unbound/unbound.conf -d
ExecReload=/usr/local/sbin/unbound-control reload
[Install]
WantedBy=multi-user.target
EOM
systemctl daemon-reload
cat << EOM > /etc/cron.monthly/update-unbound-hints.sh
#!/bin/bash
wget -q https://www.internic.net/domain/named.root -O /tmp/root.hints
if grep -q ROOT-SERVERS /tmp/root.hints ;then
mv -f /tmp/root.hints /usr/local/etc/unbound/trust/root.hints ; chmod a+r /usr/local/etc/unbound/trust/root.hints ; chown unbound:unbound /etc/unbound/trust/*
fi
EOM
chmod a+x /etc/cron.monthly/update-unbound-hints.sh
/etc/cron.monthly/update-unbound-hints.sh
rm /usr/local/etc/unbound/unbound.conf
wget -O /etc/unbound/unbound.conf https://gist.githubusercontent.com/bruvv/41577f18732b2bfb9ab18fe0581b588a/raw/7ebcd72f14b132ff90a35efeb06b18c3f04b7289/unbound.conf
systemctl daemon-reload
systemctl unmask unbound
systemctl enable unbound
systemctl restart unbound
test unbound
netstat -lnp | grep unbound
dig pi-hole.net @127.0.0.1 -p 5353
dig sigfail.verteiltesysteme.net @127.0.0.1 -p 5353
dig sigok.verteiltesysteme.net @127.0.0.1 -p 5353
service unbound status
We're going to set rate limits just in case the public gain access
cat << EOM > /etc/nginx/conf.d/00-rate-limits.conf
limit_req_zone \$binary_remote_addr zone=doh_limit:10m rate=300r/s;
EOM
Create the config - remember to replace server_name with whatever name you are using
cat << EOM > /etc/nginx/sites-available/dns-over-https
upstream dns-backend {
server 127.0.0.1:53;
keepalive 30;
}
server {
listen 80;
server_name dns.domain.com;
root /tmp/NOEXIST;
location /dns-query {
limit_req zone=doh_limit burst=50 nodelay;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header Host \$http_host;
proxy_set_header X-NginX-Proxy true;
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection "";
proxy_redirect off;
proxy_set_header X-Forwarded-Proto \$scheme;
proxy_read_timeout 86400;
proxy_pass http://dns-backend/dns-query;
}
}
EOM
sudo ln -s /etc/nginx/sites-available/dns-over-https /etc/nginx/sites-enabled/dns-over-https
sudo nginx -t
sudo systemctl reload nginx
Lets check if nginx is running correctly:
netstat -lnp | grep 80
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 4890/nginx: master
Lets setup stapeling
cat << EOM > /etc/nginx/conf.d/00-cert-stapling.conf
ssl_stapling on;
ssl_stapling_verify on;
resolver 127.0.0.1:5353;
EOM
certbot --nginx -d dns.domain.com
cat << EOM > /etc/letsencrypt/options-ssl-nginx.conf
# This file contains important security parameters. If you modify this file
# manually, Certbot will be unable to automatically provide future security
# updates. Instead, Certbot will print and log an error message with a path to
# the up-to-date file that you will need to refer to when manually updating
# this file.
ssl_session_cache shared:le_nginx_SSL:1m;
ssl_session_timeout 1440m;
ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
# Enable modern TLS cipher suites
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
# The order of cipher suites matters
ssl_prefer_server_ciphers on;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
EOM
Verify the next file:
nano /etc/nginx/sites-available/dns-over-https
change the next line add http2
listen 443 ssl http2; # managed by Certbot CHANGE THIS
systemctl reload nginx
(When prompted, do not install Pi-hole default firewall rules, make a note of the admin password when it's provided)
DNS custom 127.0.0.1#5353 DO NOT INSTALL THE WEBSERVER we use nginx.
curl -sSL https://install.pi-hole.net | bash
Change the password:
sudo pihole -a -p CHANGEME
Fix the nginx.conf file:
mv /etc/nginx/nginx.conf /etc/nginx/nginx.bak
cat << EOM > /etc/nginx/nginx.conf
#user root;
#user nginx;
user www-data;
worker_processes auto;
#load_module modules/ngx_stream_js_module.so;
error_log /var/log/nginx/error.log;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
#sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
gzip on;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/security.conf;
}
# DNS Stream Services
stream {
# DNS logging
log_format dns '$remote_addr [$time_local] $protocol "$dns_qname"';
access_log /var/log/nginx/dns-access.log dns;
# Include the NJS module
#js_include /etc/nginx/njs.d/nginx_stream.js;
# The $dns_qname variable can be populated by preread calls, and can be used for DNS routing
#js_set $dns_qname dns_get_qname;
# DNS upstream pool.
upstream dns {
zone dns 64k;
server 127.0.0.1:53;
}
upstream dot {
zone dns 64k;
server 10.0.0.30:53;
}
include /etc/nginx/streams/*.conf;
}
EOM
cat << EOM > /etc/nginx/security.conf
# security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
EOM
cat << EOM > /etc/nginx/conf.d/pihole.conf
server {
root /var/www/html;
listen 80;
server_name dnsadmin.domain.com;
autoindex off;
index pihole/index.php index.php index.html index.htm;
access_log /var/log/nginx/pihole.access.log;
location / {
expires max;
try_files $uri $uri/ =404;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
fastcgi_pass unix:/run/php/php7.4-fpm.sock;
fastcgi_param FQDN true;
auth_basic "Restricted"; # For Basic Auth
auth_basic_user_file /etc/nginx/.htpasswd; # For Basic Auth
}
location /*.js {
index pihole/index.js;
auth_basic "Restricted"; #For Basic Auth
auth_basic_user_file /etc/nginx/.htpasswd; #For Basic Auth
}
location /admin {
root /var/www/html;
index index.php index.html index.htm;
auth_basic "Restricted"; #For Basic Auth
auth_basic_user_file /etc/nginx/.htpasswd; #For Basic Auth
}
location ~ /\.ht {
deny all;
}
error_page 404 /pihole/index.php;
}
EOM
sudo nginx -t
sudo systemctl reload nginx
Verify that PiHole has the correct ip:
ifconfig|grep broad|awk -F' ' '{print $2}'
Check that that exists in:
nano /etc/pihole/setupVars.conf
Lets configure FTL:
nano /etc/pihole/pihole-FTL.conf
PRIVACYLEVEL=0
DBINTERVAL=60
RESOLVE_IPV4=yes
BLOCKINGMODE=NULL
IGNORE_LOCALHOST=yes
Setup security: https://www.htaccesstools.com/htpasswd-generator/ put it in: /etc/nginx/.htpasswd
certbot --nginx -d dnsadmin.domain.com
nano /etc/nginx/conf.d/pihole.conf
change the next line add http2
listen 443 ssl http2; # managed by Certbot CHANGE THIS
systemctl restart nginx
mkdir /etc/nginx/streams/
Now get from the DoH NGINX’s site configuration file the path to your HTTPS key and certificate.
cat << EOM > /etc/nginx/streams/dns-over-tls
stream {
upstream dns-servers {
zone dns 64k;
server XX.XX.XX.XX:53; #PIHOLE IP can be found with: netstat -lnp | grep 53 DO NOT USE 127.0.0.1 or LOCALHOST
}
server {
listen 853 ssl; # managed by Certbot
proxy_bind $remote_addr transparent;
proxy_pass dns-servers;
ssl_preread on;
ssl_certificate /etc/letsencrypt/live/dns.domain.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/dns.domain.com/privkey.pem; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
ssl_protocols TLSv1.2 TLSv1.3;
#ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-$
ssl_prefer_server_ciphers on;
ssl_handshake_timeout 10s;
ssl_session_cache shared:DoT:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
}
}
EOM
nano /etc/nginx/nginx.conf
include /etc/nginx/streams/*.conf;
sed -i ‘s/www-data/root/’ /etc/nginx/nginx.conf
usermod -a -G pihole www-data
Make sure you disable dns cache from pihole:
nano /etc/dnsmasq.d/01-pihole.conf
service pihole-FTL restart
sudo systemctl restart nginx
apt-get install fail2ban
cat << EOM > /etc/fail2ban/filter.d/pihole-dns.conf
Fail2Ban configuration file
[INCLUDES]
# Read common prefixes. If any customizations available -- read them from
# common.local
before = common.conf
[Definition]
_daemon = dnsmasq
# log example from /var/log/pihole.log
#Feb 26 04:41:28 dnsmasq[1887]: query[A] 21cl93vlx5n9p.aikoaiko.net from 67.21.36.3
#(?:DAY )?MON Day 24hour:Minute:Second(?:\.Microseconds)?(?: Year)?
# This will filter all 'query' requests.
failregex = query\[.*\].* from <HOST>$
query\[ANY\].* from <HOST>$
query\[.*\] version\.bind from <HOST>$
query\[.*\] isc\.org from <HOST>$
query\[.*\] .*shadowserver.* from <HOST>$
query\[.*\] .*shodan.io from <HOST>$
reply <HOST> is no-reverse-dns-configured\.com$
reply <HOST> is .*shodan\.io$
reply <HOST> is .*arbor-observatory.*$
reply <HOST> is .*stretchoid\.com$
reply <HOST> is .*openportstats\.com$
reply <HOST> is .*hostwindsdns\.com$
reply <HOST> is .*poneytelecom\.eu$
ignoreregex =
EOM
cat << EOM > /etc/fail2ban/jail.d/nginx.conf
[nginx-auth]
enabled = true
filter = nginx-auth
action = iptables-multiport[name=NoAuthFailures, port="http,https"]
logpath = /var/log/nginx/*error*.log
[nginx-login]
enabled = false
filter = nginx-login
action = iptables-multiport[name=NoLoginFailures, port="http,https"]
logpath = /var/log/nginx/*access*.log
[nginx-badbots]
enabled = true
filter = apache-badbots
action = iptables-multiport[name=BadBots, port="http,https"]
logpath = /var/log/nginx/*access*.log
maxretry = 1
[nginx-proxy]
enabled = true
action = iptables-multiport[name=NoProxy, port="http,https"]
filter = nginx-proxy
logpath = /var/log/nginx/*access*.log
maxretry = 0
[nginx-dos]
enabled = true
port = http
filter = nginx-dos
logpath = /var/log/nginx/*access*.log
findtime = 120
maxretry = 200
EOM
cat << EOM > /etc/fail2ban/jail.d/pihole-dns.conf
[pihole-dns]
enabled = true
port = 53
action = %(banaction)s[name=%(__name__)s-tcp, port="%(port)s", protocol="tcp", chain="%(chain)s", actname=%(banaction)s-tcp]
%(banaction)s[name=%(__name__)s-udp, port="%(port)s", protocol="udp", chain="%(chain)s", actname=%(banaction)s-udp]
logpath = /var/log/pihole.log
findtime = 60
maxretry = 5
# bantime = 3600
EOM
cat << EOM > /etc/fail2ban/jail.d/ssh.conf
[sshd]
enabled = true
#port = 22
filter = sshd
#logpath = /var/log/auth.log
#maxretry = 3
EOM
systemctl start fail2ban
systemctl enable fail2ban
to check if fail2ban works
fail2ban-client status pihole-dns
Test your DNS: https://rootcanary.org/test.html
sudo nano /etc/sysctl.conf
Change / add this:
# IP Spoofing protection
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# Ignore ICMP broadcast requests
net.ipv4.icmp_echo_ignore_broadcasts = 1
# Disable source packet routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
net.ipv6.conf.default.accept_source_route = 0
# Ignore send redirects
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
# Block SYN attacks
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 2048
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syn_retries = 5
# Log Martians
net.ipv4.conf.all.log_martians = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1
# Ignore ICMP redirects
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv6.conf.default.accept_redirects = 0
# Ignore Directed pings
net.ipv4.icmp_echo_ignore_all = 1
Secure SSH
cat << EOM > /etc/ssh/sshd_config.d/secure.conf
Protocol 2
PermitRootLogin no
DebianBanner no
EOM
Persistance bans for fail2ban:
touch /etc/fail2ban/ip.blocklist
nano /etc/fail2ban/jail.conf
Find:
bantime = X
Change it to:
bantime = -1
Edit action file:
nano /etc/fail2ban/action.d/iptables-multiport.conf
Add this to actionstart
:
cat /etc/fail2ban/ip.blocklist | while read IP; do iptables -I f2b-<name> 1 -s $IP -j DROP; done
Add this to actionban
:
echo <ip> >> /etc/fail2ban/ip.blocklist
sudo systemctl stop systemd-resolved
sudo nano /etc/systemd/resolved.conf
Add this:
[Resolve]
DNS=127.0.0.1
FallbackDNS=9.9.9.9
#Domains=
#LLMNR=no
#MulticastDNS=no
#DNSSEC=no
#Cache=yes
DNSStubListener=no
And restart:
sudo ln -sf /run/systemd/resolve/resolv.conf /etc/resolv.conf
sudo systemctl restart systemd-resolved
ldconfig
apt remove software-properties-common build-essential
apt autoremove
apt autoclean
reboot
Read extra nginx examples: https://github.com/TuxInvader/nginx-dns