Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support to run dnsproxy server/client in docker #110

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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"]
61 changes: 61 additions & 0 deletions Docker/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
## 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

Create the docker image
```
# cd dnsproxy/Docker
# docker build -t dnsproxy/AdguardHome:latest $PWD
```

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

### To start the dnsproxy as `DNS-over-TLS`, `DNS-over-HTTPS` and `DNS-over-QUIC` server
```
# cd dnsproxy/Docker/server
# docker volume create dns_cert_keys
# docker-compose up -d
```

If dnsproxy server creation fails, run this command, and chances are it will be okay
```
# docker-compose up -d --force-recreate
```

### To start the dnsproxy as client, with upstream to Adguard `DNS-over-TLS, `DNS-over-HTTPS` and `DNS-over-QUIC` server
```
# cd dnsproxy/Docker/client
# docker-compose up -d
```

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

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

### Following changes required in server/docker-compose.yml. to start dnsproxy as `DNS-over-TLS`
```
SRVPORT: "853"
MODE: "server"
PROTO: "tls"
```
### Following changes required in client/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
46 changes: 46 additions & 0 deletions Docker/client/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
version: '3'

services:
dnsproxy_quic_client:
image: dnsproxy/AdguardHome:latest
container_name: dnsproxy_quic_client
restart: always
environment:
UPSTREAM_ADDR: "quic://dns-unfiltered.adguard.com:784" # change it to your prefered quic server
RATELIMIT: "30"
EDNS: "ON"
LOCALPORT: "1111" # Any local port #
MODE: "client"
PROTO: "quic"
ports:
- 1112:1111/udp
dnsproxy_https_client:
image: dnsproxy/AdguardHome:latest
container_name: dnsproxy_https_client
restart: always
environment:
UPSTREAM_ADDR: "https://dns.adguard.com/dns-query" # change it to your prefered https server
RATELIMIT: "30"
EDNS: "ON"
LOCALPORT: "1111" # Any local port #
MODE: "client"
ports:
- 1113:1111/udp
dnsproxy_tls_client:
image: dnsproxy/AdguardHome:latest
container_name: dnsproxy_tls_client
restart: always
environment:
UPSTREAM_ADDR: "tls://dns.adguard.com" # change it to your prefered tls server
RATELIMIT: "30"
EDNS: "ON"
LOCALPORT: "1111" # Any local port #
MODE: "client"
ports:
- 1114:1111/udp
networks:
default:
driver: bridge
ipam:
config:
- subnet: 172.16.57.0/24
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 8;
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
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
74 changes: 74 additions & 0 deletions Docker/server/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
version: '3'

services:
dnsproxy_https:
image: dnsproxy/AdguardHome:latest
container_name: dnsproxy_https
restart: always
volumes:
- dns_cert_keys:/etc/letsencrypt
environment:
LISTEN: "0.0.0.0"
SRVPORT: "443"
DOMAIN: "example.org"
EMAIL: "email@example.org"
UPSTREAM_ADDR: "1.1.1.1:53"
RATELIMIT: "100"
EDNS: "ON"
LOCALPORT: "1111" # Any local port #
MODE: "server"
PROTO: "https"
ports:
- 80:80 # Port 80 is mandatory for letsencrypt to generate cert/keys successfully
- 443:443
- 1114:1111/udp
dnsproxy_quic:
image: dnsproxy/AdguardHome:latest
container_name: dnsproxy_quic
restart: always
depends_on:
- dnsproxy_https
volumes:
- dns_cert_keys:/etc/letsencrypt
environment:
LISTEN: "0.0.0.0"
SRVPORT: "784"
DOMAIN: "jiteuhub.ddns.net"
EMAIL: "email@example.org"
UPSTREAM_ADDR: "1.1.1.1:53"
RATELIMIT: "100"
EDNS: "ON"
LOCALPORT: "1111" # Any local port #
MODE: "server"
PROTO: "quic"
ports:
- 784:784/udp
- 1112:1111/udp
dnsproxy_tls:
image: dnsproxy/AdguardHome:latest
container_name: dnsproxy_tls
restart: always
depends_on:
- dnsproxy_https
volumes:
- dns_cert_keys:/etc/letsencrypt # Uncomment this line to reuse existing cert and key
environment:
LISTEN: "0.0.0.0"
SRVPORT: "853"
DOMAIN: "jiteuhub.ddns.net"
EMAIL: "email@example.org"
UPSTREAM_ADDR: "1.1.1.1:53"
RATELIMIT: "100"
EDNS: "ON"
LOCALPORT: "1111" # Any local port #
MODE: "server"
PROTO: "tls"
ports:
- 853:853
- 1113:1111/udp

# Create this volume by running "docker volume create dns_cert_keys" command
volumes:
dns_cert_keys:
external:
name: dns_cert_keys
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