Skip to content

Commit

Permalink
Added support to run dnsproxy server/client in docker
Browse files Browse the repository at this point in the history
  • Loading branch information
coolquasar committed Nov 22, 2020
1 parent 2f7a50c commit cb38dbe
Show file tree
Hide file tree
Showing 7 changed files with 255 additions and 0 deletions.
37 changes: 37 additions & 0 deletions Docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
FROM golang:1.14-alpine as builder

RUN apk --update --no-cache add \
build-base \
git \
&& rm -rf /tmp/* /var/cache/apk/*

WORKDIR ~
RUN git clone https://github.com/AdguardTeam/dnsproxy

WORKDIR dnsproxy/

RUN go build -mod=vendor -v

FROM alpine:3.12
LABEL maintainer="coolquasar@gmail.com"

RUN apk update && \
apk add bash supervisor certbot procps net-tools && \
rm -rf /tmp/* /var/cache/apk/*

COPY --from=builder /go/~/dnsproxy/dnsproxy /usr/bin

COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf

COPY letsencrypt-wrapper.sh /srv/
COPY dnsproxy.sh /srv/
COPY start.sh /srv/

VOLUME ["/var/log", "/etc/letsencrypt"]

EXPOSE "80"
EXPOSE "443"
EXPOSE "784"/udp
EXPOSE "853"

ENTRYPOINT ["/srv/start.sh"]
42 changes: 42 additions & 0 deletions Docker/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
## dnsproxy - Docker

This part of the project is to run dnsproxy in docker as server or client. The docker image pulls the current dnsproxy code, builds it and creates an alpine based docker image

Default version of docker-compose runs dnsproxy as quic server with upstream 1.1.1..1
1. Current version of dnsproxy docker supports `DNS-over-TLS, `DNS-over-HTTPS`, `DNSCrypt`, and `DNS-over-QUIC`
2. Moreover, it can work as a `DNS-over-HTTPS`, `DNS-over-TLS` or `DNS-over-QUIC` server, or a simple passthrough server in a defined port

Functionalities supported by the docker image will be sub-set of the functionalities supported by dnsproxy current code

```
# docker-compose up -d
```

### Following changes required in docker-compose.yml. to start dnsproxy as `DNS-over-HTTPS`
```
SRVPORT: "443"
MODE: "server"
PROTO: "https"
```

### Following changes required in docker-compose.yml. to start dnsproxy as `DNS-over-QUIC`
```
SRVPORT: "784"
MODE: "server"
PROTO: "quic"
```


### Following changes required in docker-compose.yml. to start dnsproxy as `DNS-over-TLS`
```
SRVPORT: "853"
MODE: "server"
PROTO: "tls"
```
### Following changes required in docker-compose.yml. to start dnsproxy as `client`
```
MODE: "client"
LOCALPORT: "1234" # Any local port number
```

Remove EDNS flag in docker-compose.yml, if EDNS support is not required
41 changes: 41 additions & 0 deletions Docker/dnsproxy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/bin/bash

COMMAND="dnsproxy"
LISTENADDR=$LISTEN
SRVPORT=$SRVPORT
CERTPATH=/etc/letsencrypt/live/$DOMAIN/fullchain.pem
KEYPATH=/etc/letsencrypt/live/$DOMAIN/privkey.pem
UPSTREAM=$UPSTREAM_ADDR
RATELIMIT=$RATELIMIT
EDNSFLAG=$EDNS
EDNSIP=$EDNSIP
PORT=$LOCALPORT
PROTOCOL=$PROTO

case $PROTOCOL in
"quic") SWITCHPORT="-q $SRVPORT --tls-crt=$CERTPATH --tls-key=$KEYPATH";;
"tls") SWITCHPORT="-t $SRVPORT --tls-crt=$CERTPATH --tls-key=$KEYPATH";;
"https") SWITCHPORT="--https-port $SRVPORT --tls-crt=$CERTPATH --tls-key=$KEYPATH";;
"dnscrypt") SWITCHPORT="-y $SRVPORT --tls-crt=$CERTPATH --tls-key=$KEYPATH";;
esac

case $MODE in
"server") LISTENSWITCH="-l $LISTENADDR";
if [[ ! -f $CERTPATH ]]
then
echo "Waiting for letsencrypt cert to be created"
sleep 30;
fi;;

"client") LISTENSWITCH=" ";
SWITCHPORT=" ";;
esac

if [[ $EDNSFLAG == "" ]]
then
$COMMAND $LISTENSWITCH $SWITCHPORT -u $UPSTREAM -r $RATELIMIT -p $PORT
else
$COMMAND $LISTENSWITCH $SWITCHPORT -u $UPSTREAM -r $RATELIMIT --edns -p $PORT
fi

#dnsproxy -l 0.0.0.0 -q 785 --tls-crt=/etc/letsencrypt/live/$DOMAIN/fullchain.pem --tls-key=/etc/letsencrypt/live/$DOMAIN/privkey.pem -u 192.168.2.1:53 -r 100 --edns -p $PORT
27 changes: 27 additions & 0 deletions Docker/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
version: '3'

services:
dnsproxy:
image: dnsproxy/AdguardHome:latest
container_name: dnsproxy
restart: always
#volumes:
#- /etc/letsencrypt:/etc/letsencrypt # Uncomment this line to reuse existing cert and key
environment:
LISTEN: "0.0.0.0"
SRVPORT: "784"
DOMAIN: "example.org" # Your domain name required
EMAIL: "email@example.org"
UPSTREAM_ADDR: "1.1.1.1:53"
RATELIMIT: "30"
EDNS: "ON"
LOCALPORT: "1111" # Any local port #
MODE: "server"
PROTO: "quic"
DRYRUN: "--dry-run" # Uncomment this option to test letsencrypt cert generation process
ports:
- 80:80 # Port 80 is mandatory for letsencrypt to generate cert/keys successfully
- 443:443
- 784:784/udp
- 853:853
- 1111:1111/udp
85 changes: 85 additions & 0 deletions Docker/letsencrypt-wrapper.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#!/bin/bash

if [[ $DEBUG ]]; then
set -x
fi

logger() {
echo "[letsencrypt] :: $(date +%x-%X) :: $@" | tee -a /var/log/letsencrypt-wrapper.log
}

get_first_time() {
local d=$1
if certbot certonly $DRYRUN --agree-tos --email $EMAIL \
-n --standalone -w /etc/letsencrypt -d $d
then
logger "First certificate got for $d"
else
logger "ERROR on first time certificate for $d"
exit 2
fi
}

renew_certs() {
certbot renew $DRYRUN -n --standalone -w /etc/letsencrypt -d "${1}"
}

check_env() {
if ! [[ $DOMAIN ]]; then
logger "ERROR! Missing domains to be used! Set DOMAIN environment variable."
exit 1
fi
if ! [[ $EMAIL ]]; then
logger "ERROR! Missing email address to use to register the domain(s) certificates."
fi
}

should_force() {
if ! [[ -f "/etc/letsencrypt/live/${1}/.doh-force" ]]; then
return 0
fi
logger "Not skipping - found file /etc/letsencrypt/live/${1}/.doh-force"
return 1
}

le_vol_mounted() {
if grep "/etc/letsencrypt/live/${1}" <(mount) > /dev/null; then
logger "A letsencrypt volume is mounted for domain ${1}"
should_force $1 && return 0
elif grep "/etc/letsencrypt" <(mount) > /dev/null; then
logger "The whole /etc/letsencrypt is mounted"
if [[ -d "/etc/letsencrypt/live/${1}" ]]; then
should_force $1 && return 0
fi
fi
return 1
}

main() {
check_env
for d in $DOMAIN; do
if le_vol_mounted ${d}; then
# Assuming that if the volume is mounted from the host
# creation and renewal is in charge of others,
# except if the .doh-force file il present in the
# directory of the certificates. In such case, the container
# will take care of renewing them.
logger "Skipping domain ${1}"
continue
fi
if [[ -f /etc/letsencrypt/live/$d/privkey.pem ]]; then
logger "Renewing certificate for $d"
renew_certs $d
else
logger "Getting first time certificates for $d"
get_first_time $d
fi
done
logger "All done, sleeping for ${WAIT_TIME:-"1d"}."
sleep ${WAIT_TIME:-"1d"}
}

while true
do
main
done
3 changes: 3 additions & 0 deletions Docker/start.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash

/usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf
20 changes: 20 additions & 0 deletions Docker/supervisord.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[supervisord]
nodaemon=true

[program:letsencrypt]
command=/srv/letsencrypt-wrapper.sh
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
stderr_logfile=/dev/fd/2
stderr_logfile_maxbytes=0
username=root
autorestart=false

[program:dnsproxy]
command=/srv/dnsproxy.sh
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
stderr_logfile=/dev/fd/2
stderr_logfile_maxbytes=0
username=root
autorestart=true

0 comments on commit cb38dbe

Please sign in to comment.