#!/usr/bin/env bash # Sync_pihole_lan.sh synchronise the /etc/pihole/lan.list file with your Subdomains # v4.0 synchro avec la zone photos-nas.ovh sur OVH via l'API # 1. liste principale des sous-domaines => subdomains.photos-nas.ovh.txt # 2. sauvegarde des lan.list de chaque pihole => lan.list.1 et lan.list.2 # 3. lan.list à jour que l'on copie sur chaque pihole # 4. on récupère via l'API la list des CNAME sur la zone photos-nas.ovh => zone.photos-nas.ovh # 5. on ajoute ou supprime les CNAME sur la zone photos-nas.ovh italic="\033[3m" underline="\033[4m" ita_under="\033[3;4m" bgd="\033[1;4;31m" red="\033[1;31m" bold="\033[1m" bold_ita="\033[1;3m" box="\033[1;41m" redbold="\033[1;31m" redbox="\033[1;41m" green="\033[0;32m" greenbold="\033[1;32m" reset="\033[0m" absent=() delete=() extra=() domain=".photos-nas.ovh" not_required=("drive" "files" "gitea" "home-assistant" "homebridge" "portainer" "tunes" "wg" "www") not_used=("ds916" "musiiic" "notif") pihole1=192.168.2.116 pihole2=192.168.2.216 ScriptPath="$(readlink -f "$0")" # /Users/bruno/Documents/Scripts/bashbirds/bashbirds.sh ScriptWorkDir="$(dirname "$ScriptPath")" # /Users/bruno/Documents/Scripts/bashbirds echo -e "${greenbold}Sync_pihole_lan.sh synchronise the /etc/pihole/lan.list file with your Subdomains ...${reset}\n" echo -e " For Pihole 6:" echo -e " - edit Pihole preferences (nano /etc/pihole/pihole.toml)" echo -e " - set etc_dnsmasq_d = true ### CHANGED, default = false" echo echo : << 'COMMENTS' COMMENTS cat < /dev/null > /dev/tcp/1.1.1.1/53 if [[ $? -ne 0 ]]; then echo -e "\n${red}No Internet connection !${reset}" echo -e "Exit !" exit 1 fi echo -e "${bold}Backup ${italic}lan.list...${reset}" # 1. Sauvegarde du /etc/pihole/lan.list de chaque pihole vers lan.list.1 et lan.list.2 ping -q -c1 "$pihole1" &>/dev/null && ssh -p51322 root@"$pihole1" "cat /etc/pihole/lan.list" > $ScriptWorkDir/lan.list.1 || echo -e "\n${red}Pihole1 is unreachable !'${reset}" ping -q -c1 "$pihole2" &>/dev/null && ssh -p51522 root@"$pihole2" "cat /etc/pihole/lan.list" > $ScriptWorkDir/lan.list.2 || echo -e "${red}Pihole2 is unreachable !'${reset}" # Si un lan.list.bak existe, on l'archive if [ -f "$ScriptWorkDir/lan.list.bak" ]; then cp "$ScriptWorkDir/lan.list.bak" /tmp/lan.list bzip2 /tmp/lan.list # lan.list.bz2 mv /tmp/lan.list.bz2 . fi # On backup le lan.list vers lan.list.bak (sinon lan.list.1 devient lan.list) if [ -f $ScriptWorkDir/lan.list ]; then cp $ScriptWorkDir/lan.list $ScriptWorkDir/lan.list.bak elif [ -f $ScriptWorkDir/lan.list.1 ]; then cp $ScriptWorkDir/lan.list.1 $ScriptWorkDir/lan.list elif [ -f $ScriptWorkDir/lan.list.2 ]; then cp $ScriptWorkDir/lan.list.2 $ScriptWorkDir/lan.list else echo "Error ! No lan.list file !" exit fi nb_dynhost=$(awk 'END { print NR }' $ScriptWorkDir/lan.list) echo -e "\n$nb_dynhost dynhost found in lan.list !" # 2. Suppression des 'not_required' et 'not_used' dans le lan.list echo -e "${bold}\nFind not_used and not_required dynhost in ${italic}lan.list...${reset}" while IFS= read -r line; do ndd=$(echo "${line}" | awk '{print $2}' | awk -F"." '{print $1}') if [[ " ${not_required[*]} " =~ " $ndd " ]] || [[ " ${not_used[*]} " =~ " $ndd " ]]; then if [[ ! "$line" == *asusrouter* ]]; then delete+=(${ndd}) fi fi done < $ScriptWorkDir/lan.list if [ ${#delete[@]} -ge 1 ]; then echo -e "${red}To delete: ${delete[@]}${reset}" b=$(echo -e "Do you want to delete ${#delete[@]} extra dynhost in ${italic}lan.list${reset} ? (y/n)") read -p "$b" choice if [ "$choice" == "y" ] || [ "$choice" == "Y" ]; then for val in ${!delete[@]} do value="${delete[$val]}$domain" value=$(echo "$value" | sed 's/\./\\\./g') # -i '' for sed BSD ; -i for sed GNU sed -i '' "/$value/d" lan.list done nb_dynhost=$(awk 'END { print NR }' lan.list) echo "$nb_dynhost dynhost found in lan.list !" fi else echo -e "${green}None !${reset}" fi # 3. Recherche des sous-domaines manquants dans lan.list (d'après subdomains.photos-nas.ovh.txt) echo -e "${bold}\nFind missing dynhost in ${italic}lan.list...${reset}" #dynhost_list=$(cat photos-nas.ovh_dns_data.txt | grep "60 IN A" | sed '1d') dynhost_list=$(cat $ScriptWorkDir/subdomains.photos-nas.ovh.txt) while IFS= read -r line; do dynhost=$(echo "${line}" | awk '{print $1}') grep -q $dynhost $ScriptWorkDir/lan.list if [ $? != 0 ]; then if [[ ! " ${not_required[*]} " =~ " $dynhost " ]] && [[ ! " ${not_used[*]} " =~ " $dynhost " ]]; then absent+=(${dynhost}) fi fi done <<< "$dynhost_list" if [ ${#absent[@]} -ge 1 ]; then echo -e "${green}Not required: ${not_required[@]}${reset}" echo -e "${italic}Not used: ${not_used[@]}${reset}" echo -e "${red}Missing: ${absent[@]}${reset}" # On supprime /tmp/temp_file.list [ -f /tmp/temp_file.list ] && rm /tmp/temp_file.list # On crée un /tmp/temp_file.list avec les sous-domaines manquants for val in ${absent[@]} do echo -e "192.168.2.57 ${val}.photos-nas.ovh ${val}" >> /tmp/temp_file.list done echo -e "\n${bold}Update ${italic}lan.list...${reset}" cat /tmp/temp_file.list # On ajoute le /tmp/temp_file.list au lan.list cat /tmp/temp_file.list >> $ScriptWorkDir/lan.list # On trie le lan.list cat $ScriptWorkDir/lan.list | sort -k2 > $ScriptWorkDir/lan.list.sorted mv $ScriptWorkDir/lan.list.sorted $ScriptWorkDir/lan.list else echo -e "${green}No new dynhost !${reset}" fi # 4. Recherche des sous-domaines supplementaires dans lan.list (d'après subdomains.photos-nas.ovh.txt) echo -e "${bold}\nRemove extra dynhost in ${italic}lan.list...${reset}" lan_list=$(cat $ScriptWorkDir/lan.list) while IFS= read -r line; do dynhost=$(echo "${line}" | awk '{print $2}' | awk -F"." '{print $1}') grep -q $dynhost $ScriptWorkDir/subdomains.photos-nas.ovh.txt if [ ! $? != 0 ]; then echo "${line}" >> /tmp/extra.txt else extra+=(${dynhost}) fi if [[ "$dynhost" == *www* ]]; then echo "${line}" >> /tmp/extra.txt #extra=( "${extra[@]/$dynhost}" ) # on supprime www et on reconstruit le tableau new_array=() for value in "${extra[@]}" do [[ $value != $dynhost ]] && new_array+=("$value") done extra=("${new_array[@]}") unset new_array fi done <<< "$lan_list" if [ ${#extra[@]} -ge 1 ]; then echo "${extra[@]}" else echo -e "${green}No extra dynhost !${reset}" fi cp /tmp/extra.txt $ScriptWorkDir/lan.list [ -f /tmp/extra.txt ] && rm /tmp/extra.txt echo echo -e "${bold}Display ${italic}lan.list. Please verify IP and subdomains.${reset}" cat $ScriptWorkDir/lan.list nb_dynhost=$(awk 'END { print NR }' $ScriptWorkDir/lan.list) echo -e "$nb_dynhost dynhost !\n" # Do you want to modify lan.list ? a=$(echo -e "Do you want to edit ${italic}lan.list${reset} ? (y/n)") read -p "$a" choice if [ "$choice" == "y" ] || [ "$choice" == "Y" ]; then nano -l $ScriptWorkDir/lan.list fi # 4. On exporte lan.list vers chaque /etc/pihole/lan.list (pihole1 et pihole2) : << 'COMMENTS2' echo "toto" COMMENTS2 b=$(echo -e "Do you want to export ${italic}lan.list${reset} to ${bold}pihole1${reset} ($pihole1) and ${bold}pihole2${reset} ($pihole2) ? (y/n)") read -p "$b" choice if [ "$choice" == "y" ] || [ "$choice" == "Y" ]; then if ping -q -c1 "$pihole1" &>/dev/null; then echo "Export to pihole1..." cat $ScriptWorkDir/lan.list | ssh -p51322 root@"$pihole1" 'cat > /etc/pihole/lan.list' [ $? != 0 ] && echo -e "${red}Error during transfer to pihole1 ($pihole1)${reset}" || echo -e "${green}Ok${reset}" # pihole 5 #echo "Restarting dns on pihole1..." #ssh -p51322 root@"$pihole1" 'pihole restartdns' # pihole 6 echo "Update the lists and flush the cache without restarting the DNS server on pihole1..." ssh -p51322 root@"$pihole1" 'pihole reloaddns' [ $? != 0 ] && echo -e "${red}Error during reloading dns on pihole1 ($pihole1)${reset}" || echo -e "${green}Ok${reset}" else echo -e "\n${red}Pihole1 is unreachable !'${reset}" fi if ping -q -c1 "$pihole2" &>/dev/null; then echo "Export to pihole2..." cat $ScriptWorkDir/lan.list | ssh -p51522 root@"$pihole2" 'cat > /etc/pihole/lan.list' [ $? != 0 ] && echo -e "${red}Error during transfer to pihole2 ($pihole2)${reset}" || echo -e "${green}Ok${reset}" # pihole 5 #echo "Restarting dns on pihole2..." #ssh -p51522 root@"$pihole2" 'pihole restartdns' # pihole 6 echo "Update the lists and flush the cache without restarting the DNS server on pihole2..." ssh -p51522 root@"$pihole2" 'pihole reloaddns' [ $? != 0 ] && echo -e "${red}Error during reloading dns on pihole2 ($pihole2)${reset}" || echo -e "${green}Ok${reset}" else echo -e "\n${red}Pihole2 is unreachable !'${reset}" fi else echo -e "OK, let's continue..." fi # 5. On compare la liste principale des sous-domaines subdomains.photos-nas.ovh.txt avec la zone photos-nas.ovh sur ovh (via l'API) # On récupère la zone photos-nas.ovh depuis ovh # a) Zones DNS -> photos-nas.ovh -> Historique des zones => photos-nas.ovh_dns_data.txt # b) API OVH -> liste des CNAME de la zone photos-nas.ovh => zone.photos-nas.ovh HTTP_QUERY="https://api.ovh.com/1.0/domain" TIME=$(curl -s https://api.ovh.com/1.0/auth/time) ZONE_FILE="zone.photos-nas.ovh" ZONE="photos-nas.ovh" source $ScriptWorkDir/ovh_secrets.txt export_zone() { HTTP_METHOD="GET" HTTP_BODY="" HTTP_QUERY="https://eu.api.ovh.com/v1/domain/zone/$ZONE/export" CLEAR_SIGN="$OVH_APP_SECRET+$OVH_CONSUMER_KEY+$HTTP_METHOD+$HTTP_QUERY+$HTTP_BODY+$TIME" SIG='$1$'$(echo -n $CLEAR_SIGN | openssl dgst -sha1 | sed -e 's/^.* //') curl --silent -X $HTTP_METHOD -H "Content-Type:application/json;charset=utf-8" -H "X-Ovh-Application:$OVH_APP_KEY" -H "X-Ovh-Timestamp:$TIME" -H "X-Ovh-Signature:$SIG" -H "X-Ovh-Consumer:$OVH_CONSUMER_KEY" --data "$HTTP_BODY" $HTTP_QUERY | sed 's/\\n/\n/g' | sed 's/\"//g' | grep 'CNAME'> $ZONE_FILE #curl --silent -X $HTTP_METHOD -H "Content-Type:application/json;charset=utf-8" -H "X-Ovh-Application:$OVH_APP_KEY" -H "X-Ovh-Timestamp:$TIME" -H "X-Ovh-Signature:$SIG" -H "X-Ovh-Consumer:$OVH_CONSUMER_KEY" --data "$HTTP_BODY" $HTTP_QUERY | sed 's/\\n/\n/g' | grep 'CNAME' # |sed 's/,//g' |sed 's/\\n/\'$'\n''/g' # | sed 's/\\n/\n/g' #echo $? if [ $? == 0 ]; then echo -e "File ${italic}$ZONE_FILE${reset} have been created from OVH $ZONE zone !" fi } remove_cname() { record="" HTTP_METHOD="GET" HTTP_BODY="" HTTP_QUERY="https://eu.api.ovh.com/v1/domain/zone/$ZONE/record?fieldType=CNAME&subDomain=$1" CLEAR_SIGN="$OVH_APP_SECRET+$OVH_CONSUMER_KEY+$HTTP_METHOD+$HTTP_QUERY+$HTTP_BODY+$TIME" SIG='$1$'$(echo -n $CLEAR_SIGN | openssl dgst -sha1 | sed -e 's/^.* //') record=$(curl --silent -X $HTTP_METHOD -H "Content-Type:application/json;charset=utf-8" -H "X-Ovh-Application:$OVH_APP_KEY" -H "X-Ovh-Timestamp:$TIME" -H "X-Ovh-Signature:$SIG" -H "X-Ovh-Consumer:$OVH_CONSUMER_KEY" --data "$HTTP_BODY" $HTTP_QUERY | sed 's/.//;s/.$//') #echo $? # 0 if [ $? == 0 ] && [ -n "$record" ]; then echo "Record number for CNAME $1 is $record !" HTTP_METHOD="DELETE" HTTP_BODY="" HTTP_QUERY="https://eu.api.ovh.com/v1/domain/zone/$ZONE/record/$record" CLEAR_SIGN="$OVH_APP_SECRET+$OVH_CONSUMER_KEY+$HTTP_METHOD+$HTTP_QUERY+$HTTP_BODY+$TIME" SIG='$1$'$(echo -n $CLEAR_SIGN | openssl dgst -sha1 | sed -e 's/^.* //') curl --silent -X $HTTP_METHOD -H "Content-Type:application/json;charset=utf-8" -H "X-Ovh-Application:$OVH_APP_KEY" -H "X-Ovh-Timestamp:$TIME" -H "X-Ovh-Signature:$SIG" -H "X-Ovh-Consumer:$OVH_CONSUMER_KEY" --data "$HTTP_BODY" $HTTP_QUERY #echo $? # 0 if [ $? == 0 ]; then echo "CNAME $1 have been successfully deleted !" else echo "Error while deleting CNAME $1 !" fi else echo "No record number found for CNAME $1 !" fi } add_cname() { HTTP_METHOD="POST" HTTP_BODY="{\"fieldType\": \"CNAME\",\"subDomain\": \"$1\",\"target\": \"photos-nas.ovh.\",\"ttl\": 0}" HTTP_QUERY="https://eu.api.ovh.com/v1/domain/zone/$ZONE/record" CLEAR_SIGN="$OVH_APP_SECRET+$OVH_CONSUMER_KEY+$HTTP_METHOD+$HTTP_QUERY+$HTTP_BODY+$TIME" SIG='$1$'$(echo -n $CLEAR_SIGN | openssl dgst -sha1 | sed -e 's/^.* //') curl --silent -X $HTTP_METHOD -H "Content-Type:application/json;charset=utf-8" -H "X-Ovh-Application:$OVH_APP_KEY" -H "X-Ovh-Timestamp:$TIME" -H "X-Ovh-Signature:$SIG" -H "X-Ovh-Consumer:$OVH_CONSUMER_KEY" --data "$HTTP_BODY" $HTTP_QUERY #echo $? # null if [ $? == 0 ]; then echo "CNAME $1 have been successfully added !" else echo "Error adding CNAME $1 !" fi HTTP_BODY="" HTTP_QUERY="https://eu.api.ovh.com/v1/domain/zone/$ZONE/refresh" CLEAR_SIGN="$OVH_APP_SECRET+$OVH_CONSUMER_KEY+$HTTP_METHOD+$HTTP_QUERY+$HTTP_BODY+$TIME" SIG='$1$'$(echo -n $CLEAR_SIGN | openssl dgst -sha1 | sed -e 's/^.* //') curl --silent -X $HTTP_METHOD -H "Content-Type:application/json;charset=utf-8" -H "X-Ovh-Application:$OVH_APP_KEY" -H "X-Ovh-Timestamp:$TIME" -H "X-Ovh-Signature:$SIG" -H "X-Ovh-Consumer:$OVH_CONSUMER_KEY" --data "$HTTP_BODY" $HTTP_QUERY #echo $? # 0 if [ $? == 0 ]; then echo "Zone $ZONE have been successfully refresh !" else echo "Error while refreshing zone $ZONE !" fi } echo -e "${bold}\nGet extra CNAME records list from zone ${italic}$ZONE${reset} (API OVH)..." export_zone #ovh_cname_list=$(cat photos-nas.ovh_dns_data.txt | grep "CNAME" | sed '1d') ovh_cname_list=$(cat $ScriptWorkDir/$ZONE_FILE) subdomains_list=$(cat $ScriptWorkDir/subdomains.photos-nas.ovh.txt) extra_ovh=() absent_ovh=() echo -e "${bold}\nFind extra CNAME records in zone ${italic}$ZONE${reset}..." while IFS= read -r line; do dynhost=$(echo "${line}" | awk '{print $1}') grep -q $dynhost $ScriptWorkDir/subdomains.photos-nas.ovh.txt if [ $? != 0 ]; then if [[ ! " ${not_required[*]} " =~ " $dynhost " ]] && [[ ! " ${not_used[*]} " =~ " $dynhost " ]]; then #echo "$dynhost présent sur ovh" extra_ovh+=(${dynhost}) fi fi # 55 sur ovh # 54 sur subdomains.photos-nas.ovh.txt # 43 sur lan.list done <<< "$ovh_cname_list" if [ ${#extra_ovh[@]} -ge 1 ]; then echo -e "${red}To delete: ${extra_ovh[@]}${reset}" for val in ${!extra_ovh[@]} do value="${extra_ovh[$val]}" b=$(echo -e "Do you want to delete ${red}$value${reset} CNAME in OVH zone ${italic}$ZONE${reset} ? (y/n)") read -p "$b" choice if [ "$choice" == "y" ] || [ "$choice" == "Y" ]; then remove_cname "$value" fi done #nb_dynhost=$(awk 'END { print NR }' lan.list) #echo "$nb_dynhost dynhost found in lan.list !" #fi else echo "None" fi echo -e "${bold}\nFind missing CNAME records in zone ${italic}$ZONE${reset}..." while IFS= read -r line; do dynhost=$(echo "${line}" | awk '{print $1}') grep -q $dynhost $ScriptWorkDir/zone.photos-nas.ovh if [ $? != 0 ]; then if [[ ! " ${not_required[*]} " =~ " $dynhost " ]] && [[ ! " ${not_used[*]} " =~ " $dynhost " ]]; then #echo "$dynhost absent sur ovh" absent_ovh+=(${dynhost}) fi fi done <<< "$subdomains_list" if [ ${#absent_ovh[@]} -ge 1 ]; then echo -e "${red}To add: ${absent_ovh[@]}${reset}" for val in ${!absent_ovh[@]} do value="${absent_ovh[$val]}" b=$(echo -e "Do you want to add ${red}$value${reset} CNAME in OVH zone ${italic}$ZONE${reset} ? (y/n)") read -p "$b" choice if [ "$choice" == "y" ] || [ "$choice" == "Y" ]; then echo "$value" add_cname "$value" fi done #nb_dynhost=$(awk 'END { print NR }' lan.list) #echo "$nb_dynhost dynhost found in lan.list !" else echo "None" fi # 6. On supprime les fichiers temporaires [ -f /tmp/temp_file.list ] && rm /tmp/temp_file.list [ -f $ScriptWorkDir/lan.list.sorted ] && rm $ScriptWorkDir/lan.list.sorted # 7. fin echo -e "\n${greenbold}Fin !${reset}" # https://www.nas-forum.com/forum/topic/80160-multiples-dynhost-ovh/#comment-1319505612