| 1 | #!/bin/sh |
| 2 | # Contributor: Stuart Cardall <[email protected]> |
| 3 | # dnscrypt-proxy setup script to choose DNS Resolver & install / configure / remove DNS Caching |
| 4 | ############################################################################################### |
| 5 | |
| 6 | config='/etc/conf.d/dnscrypt-proxy' |
| 7 | dhcpconfig='/etc/dhcp/dhclient.conf' |
| 8 | unboundconfig='/etc/unbound/unbound.conf' |
| 9 | |
| 10 | SCRIPT=$(echo "`basename $0`") |
| 11 | |
| 12 | NORMAL="\033[1;0m" |
| 13 | STRONG="\033[1;1m" |
| 14 | RED="\033[1;31m" |
| 15 | GREEN="\033[1;32m" |
| 16 | |
| 17 | print_question() { |
| 18 | local prompt="${STRONG}$1 ${RED}$2${NORMAL}" |
| 19 | printf "${prompt} %s" |
| 20 | } |
| 21 | |
| 22 | print_strong() { |
| 23 | local prompt="${STRONG}$1 ${RED}$2${NORMAL}" |
| 24 | printf "${prompt} %s\n" |
| 25 | } |
| 26 | |
| 27 | print_green() { |
| 28 | local prompt="${GREEN}${STRONG}$1 ${NORMAL}" |
| 29 | printf "${prompt} %s\n" |
| 30 | } |
| 31 | |
| 32 | print_table() { |
| 33 | local choice="${RED}${STRONG}$1${NORMAL}" |
| 34 | local resolver="${STRONG}$2" |
| 35 | local location="${GREEN}$3" |
| 36 | printf "${choice} ${resolver} ${location} %s\n" |
| 37 | } |
| 38 | |
| 39 | die() { |
| 40 | print_table "ERROR:" "$1" > /dev/null 1>&2 |
| 41 | exit 1 |
| 42 | } |
| 43 | |
| 44 | restart_interface(){ |
| 45 | INTERFACES=$(echo | ifconfig | grep "Link encap" | sed '/lo/d' | cut -d"L" -f1) |
| 46 | print_question "\nChoose external interface to restart from the following:" |
| 47 | print_question "\n\n$INTERFACES" "[ default: eth0 ]" |
| 48 | |
| 49 | while : |
| 50 | do |
| 51 | read RESTART |
| 52 | # Sanitize input |
| 53 | export RESTART_CLEAN="`echo "${RESTART}" | tr -cd '[[:alnum:]]'`" |
| 54 | |
| 55 | if [ ! $RESTART ] ;then |
| 56 | RESTART_CLEAN=eth0 |
| 57 | fi |
| 58 | |
| 59 | # tr will strip invalid input to nothing which passes grep |
| 60 | if [ "$RESTART_CLEAN" != "" ] && echo "$INTERFACES" | grep -e "$RESTART_CLEAN" 1>/dev/null; then |
| 61 | break |
| 62 | else |
| 63 | #move the cursor & clear the line |
| 64 | echo -en "\033[1A\033[28C\022[K" |
| 65 | fi |
| 66 | done |
| 67 | } |
| 68 | |
| 69 | choose_ip(){ |
| 70 | IPADDR=$(ifconfig |grep -B1 "inet addr" |awk '{ if ( $1 == "inet" ) { print $2 } else if ( $2 == "Link" ) { printf "%s:" ,$1 } }' |awk -F" " '{ print $1 ": " $3 }'| sed 's/addr//') |
| 71 | if echo "$IPADDR" | grep -e "127.0.0.2" 1>/dev/null && which unbound 1> /dev/null; then |
| 72 | defaultip='127.0.0.2' |
| 73 | IPADDR_CHOICE=$(echo "$IPADDR" | sed '/lo::127.0.0.1:/d') |
| 74 | else |
| 75 | defaultip='127.0.0.1' |
| 76 | IPADDR_CHOICE=$(echo "$IPADDR" | sed '/lo:1::127.0.0.2:/d') |
| 77 | fi |
| 78 | |
| 79 | print_question "\nChoose Dnscrypt IP from the following addresses:\n" |
| 80 | print_question "\n$IPADDR_CHOICE\t" "[ default: $defaultip ]" |
| 81 | |
| 82 | while : |
| 83 | do |
| 84 | read IP |
| 85 | # Sanitize input |
| 86 | export IP_CLEAN="`echo "${IP}" | tr -cd '[[:xdigit:]] [:] [.]'`" |
| 87 | if [ ! $IP ]; then |
| 88 | IP_CLEAN=$defaultip |
| 89 | fi |
| 90 | # tr will strip invalid input to nothing which passes grep |
| 91 | if [ "$IP_CLEAN" != "" ] && echo "$IPADDR_CHOICE" | grep -e "$IP_CLEAN" 1>/dev/null; then |
| 92 | print_green "\nIP: $IP_CLEAN Selected"; break |
| 93 | else |
| 94 | #move the cursor & clear the line |
| 95 | echo -en "\033[1A\033[49C\033[K" |
| 96 | fi |
| 97 | done |
| 98 | } |
| 99 | |
| 100 | choose_port(){ |
| 101 | if grep -e "127.0.0.2" /etc/network/interfaces 1>/dev/null; then |
| 102 | defaultport=40 |
| 103 | else |
| 104 | defaultport=53 |
| 105 | fi |
| 106 | |
| 107 | print_question "\nChoose Dnscrypt Port:" "[ default: $defaultport ]" |
| 108 | while : |
| 109 | do |
| 110 | read DNSPORT |
| 111 | # Sanitize input to an integer and assign to new variable |
| 112 | export DNSPORT_CLEAN="`echo "${DNSPORT}" | tr -cd '[0-9]'`" |
| 113 | if [ ! $DNSPORT ]; then |
| 114 | DNSPORT_CLEAN=$defaultport |
| 115 | fi |
| 116 | |
| 117 | if [ $DNSPORT_CLEAN -gt 0 2>/dev/null ] && [ $DNSPORT_CLEAN -le 65535 2>/dev/null ]; then |
| 118 | print_green "\nPort: $DNSPORT_CLEAN Selected"; break |
| 119 | else |
| 120 | #move the cursor & clear the line |
| 121 | echo -en "\033[1A\033[39C\033[K" |
| 122 | fi |
| 123 | done |
| 124 | } |
| 125 | |
| 126 | update_unbound(){ |
| 127 | if [ -f $unboundconfig ]; then |
| 128 | if grep "Settings from "$SCRIPT"" $unboundconfig 1>/dev/null; then |
| 129 | #update forward zone |
| 130 | START=$(sed -n "\%Settings from $SCRIPT%=" $unboundconfig) |
| 131 | LINE=$(expr $START + 3 ) |
| 132 | sed "$LINE c \ forward-addr: $IP_CLEAN@$DNSPORT_CLEAN" $unboundconfig -i |
| 133 | |
| 134 | else |
| 135 | # allow querying of localhost |
| 136 | START=$(sed -n '/do-not-query-localhost:/=' $unboundconfig) |
| 137 | sed "$START c \do-not-query-localhost: no #set by $SCRIPT" $unboundconfig -i |
| 138 | # create catch all forward zone |
| 139 | echo -e "##### Settings from $SCRIPT #####" >> $unboundconfig |
| 140 | echo -e 'forward-zone:' >> $unboundconfig |
| 141 | echo -e ' name: "."' >> $unboundconfig |
| 142 | echo -e " forward-addr: $IP_CLEAN@$DNSPORT_CLEAN" >> $unboundconfig |
| 143 | fi |
| 144 | print_strong "\n$unboundconfig settings updated to:" |
| 145 | print_green "--------------------------------------------------------" |
| 146 | print_table "do-not-query-localhost: no" |
| 147 | print_table "" |
| 148 | print_table 'forward-zone:' |
| 149 | print_table ' name: "."' |
| 150 | print_table " forward-addr: $IP_CLEAN@$DNSPORT_CLEAN" |
| 151 | print_green "--------------------------------------------------------" |
| 152 | fi |
| 153 | } |
| 154 | |
| 155 | restart_services(){ |
| 156 | # add / restart services - dnscrypt must be restarted first |
| 157 | echo "" |
| 158 | for srv in "dnscrypt-proxy" "unbound"; do |
| 159 | if which $srv 1> /dev/null; then |
| 160 | rc-status default | grep $srv 1> /dev/null |
| 161 | if [ "$?" != "0" ]; then |
| 162 | print_green "Adding $srv to Default Run Level" |
| 163 | rc-update add $srv default |
| 164 | fi |
| 165 | rc-service $srv restart |
| 166 | fi |
| 167 | done |
| 168 | } |
| 169 | |
| 170 | modify_config(){ |
| 171 | choose_ip; choose_port |
| 172 | |
| 173 | # update dnscrypt listening ip & port |
| 174 | LINE=$(sed -n '/DNSCRYPT_LOCALIP=/=' $config) |
| 175 | sed "$LINE c DNSCRYPT_LOCALIP=$IP_CLEAN:$DNSPORT_CLEAN" $config -i |
| 176 | |
| 177 | # update dhclient.conf |
| 178 | if [ -f $dhcpconfig ]; then |
| 179 | if grep 'supersede domain-name-servers' $dhcpconfig 1>/dev/null; then |
| 180 | LINE=$(sed -n '/supersede domain-name-servers/=' $dhcpconfig) |
| 181 | sed "$LINE c supersede domain-name-servers $IP" $dhcpconfig -i |
| 182 | else |
| 183 | echo "supersede domain-name-servers $IP" >> $dhcpconfig |
| 184 | fi |
| 185 | fi |
| 186 | |
| 187 | # update resolv.conf & unbound |
| 188 | LINE=$(sed -n '/nameserver/=' /etc/resolv.conf) |
| 189 | sed "$LINE c nameserver 127.0.0.1" /etc/resolv.conf -i |
| 190 | if [ "$removecache" != "Y" ] && [ "$removecache" != "y" ]; then |
| 191 | update_unbound |
| 192 | fi |
| 193 | |
| 194 | restart_interface |
| 195 | |
| 196 | print_strong "\n/etc/conf.d/dnscrypt-proxy Listening Address updated to:" |
| 197 | print_green "--------------------------------------------------------" |
| 198 | print_table "DNSCRYPT_LOCALIP=$IP_CLEAN:$DNSPORT_CLEAN" |
| 199 | print_green "--------------------------------------------------------\n" |
| 200 | } |
| 201 | |
| 202 | rm_loopback(){ |
| 203 | START=$(sed -n "\%Settings from $SCRIPT%=" /etc/network/interfaces) |
| 204 | LINE=$(expr $START + 4) |
| 205 | sed -i ''$START','$LINE'd' /etc/network/interfaces |
| 206 | print_green "2nd Loopback interface removed" |
| 207 | } |
| 208 | |
| 209 | # END Functions ################################################################################### |
| 210 | |
| 211 | # Do some sanity checking. |
| 212 | if [ $(/usr/bin/id -u) != "0" ]; then |
| 213 | die 'DNScrypt Setup must be run by root' |
| 214 | fi |
| 215 | |
| 216 | ##### Download DNS Resolver details ################################################################ |
| 217 | |
| 218 | url='https://raw.githubusercontent.com/dyne/dnscrypt-proxy/master/dnscrypt-resolvers.csv' |
| 219 | output='/tmp/dnscrypt.list' |
| 220 | |
| 221 | echo -e "Retrieving current list of free DNS Resolvers\n" |
| 222 | wget -q --no-check-certificate $url -O $output |
| 223 | sed -i 's|\"||g' $output |
| 224 | |
| 225 | totalservers=$(cat $output | tail -n +2 | wc -l) |
| 226 | |
| 227 | if [ $totalservers = 0 ]; then |
| 228 | die "Could not contact $url" |
| 229 | fi |
| 230 | |
| 231 | # colour table ################################################################################## |
| 232 | |
| 233 | colourheading=$(awk 'BEGIN { format = "%-8s%-35s%-30s%-10s%-10s%-10s%-25s\n" |
| 234 | printf format, "#", "Name", "Location", "DNSSEC", "No Logs", "Namecoin", "Resolver Address" }') |
| 235 | |
| 236 | colourline=$(awk 'BEGIN { format = "%-8s%-35s%-30s%-10s%-10s%-10s%-25s\n" |
| 237 | printf format, "----", "----------------------------------------", "------------------------------", "----------",\ |
| 238 | "----------", "----------", "-----------------------------------" }') |
| 239 | |
| 240 | print_green "$colourheading" |
| 241 | print_green "$colourline" |
| 242 | |
| 243 | tmpfile=$(mktemp) |
| 244 | |
| 245 | cat $output | awk 'BEGIN{FS=""}{gsub(/," " /," "); print}' | tail -n +2 > $tmpfile |
| 246 | |
| 247 | awk -F"," 'BEGIN {format="%-8s%-35s%-30s%-10s%-10s%-10s%-25s\n"}{ printf format, "["NR"]",$1,$4,$8,$9,$10,$11}' $tmpfile |
| 248 | |
| 249 | print_green "$colourline" |
| 250 | print_question "Please choose a DNS Resolver for dnscrypt-proxy to query:" "[1 - $totalservers]" |
| 251 | |
| 252 | ###### Process Input ############################################################################# |
| 253 | |
| 254 | while : |
| 255 | do |
| 256 | read DNS |
| 257 | # Sanitize input to an integer |
| 258 | export DNS_CLEAN="`echo "${DNS}" | tr -cd '[0-9]'`" |
| 259 | |
| 260 | if [ $DNS_CLEAN -gt 0 2>/dev/null ] && [ $DNS_CLEAN -le $totalservers 2>/dev/null ]; then |
| 261 | break |
| 262 | else |
| 263 | #move the cursor & clear the line |
| 264 | echo -en "\033[1A\033[67C\033[K" |
| 265 | fi |
| 266 | done |
| 267 | |
| 268 | RESOLVER=$(cat $tmpfile | tr -d "\"" | tr "," ";" | sed -n "$DNS_CLEAN"p |awk -F';' '{print $11}') |
| 269 | PROVIDER=$(cat $tmpfile | tr -d "\"" | tr "," ";" | sed -n "$DNS_CLEAN"p |awk -F';' '{print $12}') |
| 270 | PUBKEY=$(cat $tmpfile | tr -d "\"" | tr "," ";" | sed -n "$DNS_CLEAN"p |awk -F';' '{print $13}') |
| 271 | |
| 272 | ######## END Changes ########################################################################### |
| 273 | |
| 274 | if [ ! -f "$config" ]; then |
| 275 | touch $config |
| 276 | echo "DNSCRYPT_LOGFILE=/var/log/dnscrypt-proxy/dnscrypt-proxy.log" >> $config |
| 277 | echo "DNSCRYPT_LOCALIP=127.0.0.1:53" >> $config |
| 278 | fi |
| 279 | |
| 280 | # remove existing Resolver config |
| 281 | if grep "RESOLVER" $config 1> /dev/null; then |
| 282 | sed -e '/RESOLVER/d' -e '/PROVIDER/d' -e '/PUBKEY/d' $config -i |
| 283 | fi |
| 284 | |
| 285 | # update Resolver config |
| 286 | echo "RESOLVER=$RESOLVER" >> $config |
| 287 | echo "PROVIDER=$PROVIDER" >> $config |
| 288 | echo "PUBKEY=$PUBKEY" >> $config |
| 289 | |
| 290 | print_strong "\nResolver Settings updated in:" "$config" |
| 291 | print_green "---------------------------------------------------------------------------------------------" |
| 292 | print_table "RESOLVER :" "$RESOLVER" |
| 293 | print_table "PROVIDER :" "$PROVIDER" |
| 294 | print_table "PUBLIC KEY :" "$PUBKEY" |
| 295 | print_green "---------------------------------------------------------------------------------------------" |
| 296 | |
| 297 | # install unbound |
| 298 | if ! which unbound 1> /dev/null; then |
| 299 | print_question "Install Unbound (Caching DNS Server)" "[ Y / N: Default ]" |
| 300 | read installsrv |
| 301 | if [ "$(echo $installsrv | tr '[A-Z]' '[a-z]')" = "y" ]; then |
| 302 | apk add -q unbound |
| 303 | else |
| 304 | echo "nameserver 127.0.0.1" > /etc/resolv.conf |
| 305 | fi |
| 306 | fi |
| 307 | |
| 308 | # check for / setup secondary loopback for dns caching |
| 309 | if which unbound 1> /dev/null && ! grep "address 127.0.0.2" /etc/network/interfaces 1> /dev/null; then |
| 310 | IP=127.0.0.2 |
| 311 | echo "##### Settings from $SCRIPT #####" >> /etc/network/interfaces |
| 312 | echo "auto lo:1" >> /etc/network/interfaces |
| 313 | echo "iface lo:1 inet static" >> /etc/network/interfaces |
| 314 | echo "address $IP" >> /etc/network/interfaces |
| 315 | echo "netmask 255.0.0.0" >> /etc/network/interfaces |
| 316 | ifconfig lo:1 $IP up |
| 317 | fi |
| 318 | |
| 319 | # modify caching |
| 320 | if grep "address 127.0.0.2" /etc/network/interfaces 1> /dev/null && [ ! $installsrv ]; then |
| 321 | print_question "\nRemove DNS Caching (Unbound) / Secondary loopback device ?" "[ Y / N: Default ]"; read removecache |
| 322 | if [ "$(echo $removecache | tr '[A-Z]' '[a-z]')" = "y" ]; then |
| 323 | # remove loopback settings |
| 324 | rm_loopback |
| 325 | echo -e ""; rc-service unbound stop; apk del unbound |
| 326 | else |
| 327 | print_green "\nSecondary Loopback for DNS Caching configured @ 127.0.0.2" |
| 328 | IP=127.0.0.2 |
| 329 | fi |
| 330 | fi |
| 331 | |
| 332 | # modify ip / ports |
| 333 | if [ $installsrv ] || [ "$(echo $removecache | tr '[A-Z]' '[a-z]')" = "y" ]; then |
| 334 | modify_config |
| 335 | elif grep -q 127.0.0.2 /etc/network/interfaces && ! which unbound 1> /dev/null; then |
| 336 | rm_loopback |
| 337 | kill $(cat /var/run/unbound/unbound.pid) |
| 338 | modify_config |
| 339 | else |
| 340 | print_question "\nModify dnscrypt-proxy ip / port ?" "[ Y / N: default ]"; read updateip |
| 341 | if [ "$(echo $updateip | tr '[A-Z]' '[a-z]')" = "y" ]; then |
| 342 | modify_config |
| 343 | fi |
| 344 | fi |
| 345 | |
| 346 | if [ "$RESTART_CLEAN" != "" ]; then |
| 347 | ifdown $RESTART_CLEAN && ifup $RESTART_CLEAN |
| 348 | print_green "Interface $RESTART_CLEAN restarted" |
| 349 | fi |
| 350 | |
| 351 | restart_services |
| 352 | |
| 353 | exit 0 |
| 354 | |