diff --git a/README.md b/README.md index d2cf9fb..b5d0222 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,44 @@ # synchole -Shell script to syncronize two or more [pi-hole](https://pi-hole.net) servers. It replicates all changes to _blacklist.txt_, _whitelist.txt_, _regex.txt_ and runs `updateGravity` on all members. \ No newline at end of file +Shell script to syncronize two or more [pi-hole](https://pi-hole.net) servers. It replicates all changes to _blacklist.txt_, _whitelist.txt_, _regex.list_, _/etc/hosts_ and runs `updateGravity`/`restartdns` on all members. + + +## Setup + +1. Login via SSH to the `MASTER` server. +2. Create symlinks for all files you want to sync within the webroot of the `MASTER` pihole. +``` +cd /var/www/html +sudo mkdir synchole +cd synchole +sudo ln -s /etc/pihole/whitelist.txt . +sudo ln -s /etc/pihole/blacklist.txt . +sudo ln -s /etc/pihole/regex.list . +sudo ln -s /etc/hosts . +``` +3. Login via SSH to the `SLAVE` server. +4. Install synchole on the `SLAVE` server. +``` +cd /opt +sudo git clone https://socialg.it/david/synchole.git +``` +5. Configure the synchole script. +``` +sudo vim /opt/synchole.sh +``` +The config section is at the top of the script. +6. Create a cron job for synchole on the `SLAVE` server. +``` +sudo crontab -e +``` +Example cronjob: `*/5 * * * * /opt/synchole/synchole.sh -q` (this runs the synchole every 5 minutes) +7. Setup postfix to send notifications (from cron) on the `SLAVE` server. +``` + +``` +8. Repeat steps 3 through 5 for additional `SLAVE` servers. +9. Happy syncholeing! + + +## Resources +- https://discourse.pi-hole.net/t/what-files-does-pi-hole-use/1684 diff --git a/synchole.sh b/synchole.sh new file mode 100755 index 0000000..76f7a18 --- /dev/null +++ b/synchole.sh @@ -0,0 +1,204 @@ +#!/bin/bash + +# +# synchole.sh +# + +# **** configuration **** +LISTS="whitelist.txt blacklist.txt regex.list" #pihole files to sync +LISTS_PATH="/etc/pihole" #pihole files path +HOSTS="hosts" #hosts file +HOSTS_PATH="/etc" #hosts file path +SYNC_HOSTS=1 #enable sync of the hosts file (1=on, 0=off) +MASTER="10.1.3.2" #IP or hostname of the MASTER server +DEPENDENCIES="wget" +DEBUG=1 #enable verbose output (1=on, 0=off) +BACKUP_PATH="/etc/pihole/synchole-backups" #path to backups +TEMP_PATH="/tmp/synchole-downloads" #path to list downloads +TIMESTAMP=$(date +"%Y%m%d%H%M") + + +# **** functions **** + +## check for elevated privileges +amiroot() { + if [ $(whoami) != "root" ]; then + return 1 + fi +} + +## check for local environment +amipihole() { + if [ ! $(which pihole) ]; then + return 1 + fi +} + +## check if system is debian based +amidebian() { + if [ ! $(which apt-get) ]; then + return 1 + fi +} + +## output handling +log() { + case $1 in + "error") + level="\033[31m✗\033[0m" + sleep 0.2; echo -e "[$level] $2" + ;; + "warn") + level="\033[33m!\033[0m" + sleep 0.2; echo -e "[$level] $2" + ;; + "debug") + if [ $DEBUG -eq 1 ]; then + level="\033[2md\033[0m" + sleep 0.2; echo -e "[$level] $2" + fi + ;; + *) + level="\033[32m✓\033[0m" + sleep 0.2; echo -e "[$level] $*" + ;; + esac +} + +## update package sources +update_repos() { + apt-get update > /dev/null + if [ $? -ne 0 ]; then + return 1 + fi +} + +## install debian package +install() { + if [ -n $1 ]; then + apt-get install -y $1 &> /dev/null + if [ $? -ne 0 ]; then + return 1 + fi + else + return 1 + fi +} + +# **** start of script **** + +log "Mission takeoff!" + +## preflight checks +if ! amiroot; then + log error "You must be root. Exiting." + exit 1 +else + log debug "I am root. Continuing." +fi + +if ! amidebian; then + log error "I am no debian based system. Exiting." + exit 1 +else + log debug "I am debian based. Continuing." +fi + +if ! amipihole; then + log error "I am no pihole. Exiting." + exit 1 +else + log debug "I am a pihole system. Continuing." +fi + +log "Preflight checks passed." + +##installing missing dependencies +for DEPENDENCY in $DEPENDENCIES; do + if install $DEPENDENCY; then + log debug "$DEPENDENCY successfully installed. Continuing." + else + log error "$DEPENDENCY could not be installed. Exiting." + exit 1 + fi +done + +log debug "All dependencies available. Continuing." + +#backup local and download remote files from MASTER +if [ ! -d $BACKUP_PATH ]; then + log debug "$BACKUP_PATH does not exist. Creating." + mkdir -p $BACKUP_PATH + if [ $? -ne 0 ]; then + log error "Something went wrong while creating $BACKUP_PATH. Exiting." + exit 1 + fi +else + log debug "$BACKUP_PATH exists. Continuing" +fi + +if [ ! -d $TEMP_PATH ]; then + mkdir -p $TEMP_PATH + if [ $? -ne 0 ]; then + log error "Something went wrong while creating $TEMP_PATH. Exiting." + exit 1 + fi +else + log debug "$TEMP_PATH exists. Continuing." +fi + +CHANGES=0 +for LIST in $LISTS; do + if [ -r $LISTS_PATH/$LIST ]; then + cp $LISTS_PATH/$LIST $BACKUP_PATH/$LIST\_$TIMESTAMP + if [ $? -ne 0 ]; then + log error "Something went wrong while backing up $LIST. Exiting." + exit 1 + else + log "Successfully backed up $LIST." + fi + else + log warn "$LIST not found. Skipping." + fi + + wget http://$MASTER/synchole/$LIST -q -O $TEMP_PATH/$LIST + if [ $? -ne 0 ]; then + log error "Something went wrong while downloading http://$MASTER/synchole/$LIST. Exiting." + exit 1 + else + log "Successfully downloaded $LIST." + fi + + diff $TEMP_PATH/$LIST $LISTS_PATH/$LIST + if [ $? -ne 0 ]; then + cp $TEMP_PATH/$LIST $LISTS_PATH/$LIST + if [ $? -ne 0 ]; then + log error "Something went wrong installing updated file to $LIST_PATH/$LIST" + else + log debug "Successfully installed updated $LIST. Continuing." + CHANGES=1 + fi + else + log warn "No changes in $LIST. Skipping." + fi +done + +#run updateGravity +if [ $CHANGES -eq 1 ]; then + pihole -g > /dev/null + if [ $? -ne 0 ]; then + log error "Something went wrong while updating Gravity. Exiting." + else + log "Successfully updated Gravity." + fi +else + log warn "No files have been changed. Skipping Gravity update." +fi + +#sync hosts +#run restartdns (if hosts updates) + +log "Touchdown! Mission success." +exit 0 + +# **** end of script ****