AdguardTeam / AdGuardHome

Network-wide ads & trackers blocking DNS server
https://adguard.com/adguard-home/overview.html
GNU General Public License v3.0
25.68k stars 1.84k forks source link

Setting to allow serving DNS-over-HTTPS unencrypted #1009

Closed yegle closed 3 years ago

yegle commented 5 years ago

Problem Description

I have a public IP and I already have a reverse proxy (with wildcard certificate) in place to serve different websites.

Adguard Home's DoH support seem to only provide encrypted service, and have no option to provide non-encrypted plaintext HTTP, which means I need to reverse proxy HTTPS traffic to HTTPS backend if I want to keep my existing reverse proxy.

Proposed Solution

Adguard Home should be able to provide the same HTTP request handler in plaintext HTTP, so that I can terminate HTTPS connection using my reverse proxy and forward to backend via HTTP.

Alternatives Considered

Reverse proxy HTTPS traffic in HTTPS.

Additional Information

Full disclosure: I know Adguard Home have closed relationship with CoreDNS and this FR has been rejected: https://github.com/coredns/coredns/issues/3257

But I still think this is a valid feature and should be supported:

  1. It's very common to terminate SSL and serve HTTP service on different server (or in the world of containers, on different containers). Requiring HTTPS on the server serving HTTP means the certificate is unnecessarily widely distributed, and additional step each time you renew your certificate.
  2. Go's HTTPS support lacks OCSP stapling support which is crucial for a DoH server (See https://tools.ietf.org/html/rfc8484#section-10). This can easily mitigated by terminating SSL with tools like nginx.

I hope you are convinced this is a good idea :-)

ameshkov commented 5 years ago

Well, I see no problem with this.

Please submit a pull request.

Here's where you need to make the change: https://github.com/AdguardTeam/AdGuardHome/blob/master/home/control.go#L521

yegle commented 5 years ago

Great! I'll see if I can do it this weekend.

Akruidenberg commented 5 years ago

Great! I'll see if I can do it this weekend.

Any progress?

yegle commented 5 years ago

Sorry I was distracted. Will send a PR this weekend.

yegle commented 5 years ago

FWIW, I think the user should also be able to enable DNS-over-HTTP via the settings page. That would take a while to get right as I'm not very familiar with the frontend stuff.

yegle commented 5 years ago

:-(

It looks like the UI is implemented in React, which I have 0 knowledge of. I guess I'll leave this up to you to implement.

What I want to see in the settings page:

  1. One option to enable DoT
  2. One option to enable HTTPS, with two radio button "Enable HTTPS" and "Disable HTTPS", but provide a checkbox under "Disable HTTPS" saying "enable DNS-over-HTTPS on HTTP port"
OMICRON3069 commented 5 years ago

If AGH serves DNS over HTTP behind a reverse proxy, maybe it's a good idea to recognize client's real IP from http header (i.e. X-Real-IP and X-Forwarded-For) for request coming from localhost?

yegle commented 5 years ago

That would be my expectation yes.

ameshkov commented 5 years ago

@yegle I suggest starting with something really simple -- not expose this to the UI at all, just add a configuration property that will enable this behavior

Akruidenberg commented 4 years ago

Is also possible to do this with DOT?

ameshkov commented 4 years ago

@Akruidenberg it does not make much sense to do it for DOT. DOT without encryption is simply plain DNS over TCP which is already supported.

szolin commented 4 years ago

ec8fe0b40c5f0cf48ad2c94174e3c23898c86428 Is there something else we should do on this issue?

ameshkov commented 4 years ago

@szolin well, I think we should make it possible to change it in the UI.

Let's discuss it later, I've assigned it to v0.104

Akruidenberg commented 4 years ago

Is this already implemented in version 0.100.9? The option is available in the config file. Which docker labels are needed for traefik to get this working? The example is only for nginx. Also, when i set the value from false to true, how can i see if its working? Nothing changed in the web interface.

szolin commented 4 years ago

Yes, it's implemented. But there's no way to set in via UI, you have to edit yaml file: https://github.com/AdguardTeam/AdGuardHome/wiki/Configuration

  • allow_unencrypted_doh - Allow DOH queries via unencrypted HTTP (e.g. for reverse proxying)
Akruidenberg commented 4 years ago

Yes, it's implemented. But there's no way to set in via UI, you have to edit yaml file: https://github.com/AdguardTeam/AdGuardHome/wiki/Configuration

  • allow_unencrypted_doh - Allow DOH queries via unencrypted HTTP (e.g. for reverse proxying)

Maybe it's an good idea to write a article on the wiki with some examples with nginx and traefik. is a bit difficult to configure to get it working.

Akruidenberg commented 4 years ago

Yes, it's implemented. But there's no way to set in via UI, you have to edit yaml file: https://github.com/AdguardTeam/AdGuardHome/wiki/Configuration

  • allow_unencrypted_doh - Allow DOH queries via unencrypted HTTP (e.g. for reverse proxying)

Maybe it's an good idea to write a article on the wiki with some examples with nginx and traefik. is a bit difficult to configure to get it working.

Got dns over https working now. Not to difficult. set traefik to route over port 80 (http) and set "allow_unencrypted_doh" and the SNI value to true.

However, how to get Traefik 2 working with dns over tls. Traefik version 2 supports tcp. I dont know how to set my compose file to get it working.

This is my config:

  traefik2:
    container_name: traefik2
    image: traefik:cantal
    restart: always
    command:
      - --global.checkNewVersion=true
      - --global.sendAnonymousUsage=false
      - --entryPoints.http.address=:80
      # https://www.reddit.com/r/docker/comments/c1wrep/traefik_reverse_proxy_question_docker_overlay/
#      - --entrypoints.http.forwardedHeaders.trustedIPs=127.0.0.1/31, 192.168.90.1/24
#      - --entrypoints.http.proxyProtocol=true
#      - --entrypoints.http.proxyProtocol.trustedIPs=127.0.0.1/31, 192.168.90.1/24
      - --entryPoints.https.address=:443
      - --entryPoints.tls.address=:853
      - --api=true
#      - --api.insecure=true
#      - --serversTransport.insecureSkipVerify=false
      - --log=true
      - --log.level=INFO # (Default: error) DEBUG, INFO, WARN, ERROR, FATAL, PANIC
      - --accessLog=true
      - --accessLog.filePath=/var/log/docker/traefik.log
      - --accessLog.bufferingSize=100 # Configuring a buffer of 100 lines
      - --accessLog.filters.statusCodes=400-499
      - --providers.docker=true
      - --providers.docker.defaultrule=Host(`{{ index .Labels "com.docker.compose.service" }}.$DOMAINBASE`)
      - --providers.docker.exposedByDefault=false
      - --providers.docker.network=reverseproxy
      - --providers.docker.endpoint=tcp://dockersock-proxy:2375
      - --providers.docker.swarmMode=false
      - --providers.file.directory=/rules
      - --providers.file.watch=true
      - --certificatesresolvers.ovh.acme.dnschallenge=true
#      - --certificatesResolvers.ovh.acme.caServer=https://acme-staging-v02.api.letsencrypt.org/directory # uncomment when testing
      - --certificatesResolvers.ovh.acme.email=${SMTP_DEST_EMAIL}
      - --certificatesResolvers.ovh.acme.storage=/acme.json
      - --certificatesResolvers.ovh.acme.dnsChallenge.provider=ovh
    networks:
      - reverseproxy
      - dockersock-proxy
    user: ${PUID}
    ports:
      - "80:80"
      - "443:443"
      - "853:853"
    volumes:
      - $USERDIR/traefik2/rules:/rules
      - $USERDIR/traefik2/acme.json:/acme.json
      - $USERDIR/traefik2/traefik.log:/var/log/docker/traefik.log
    environment:
      OVH_ENDPOINT: ovh-eu
      OVH_APPLICATION_KEY: ${OVH_APPLICATION_KEY}
      OVH_APPLICATION_SECRET: ${OVH_APPLICATION_SECRET}
      OVH_CONSUMER_KEY: ${OVH_CONSUMER_KEY}
    sysctls:
      net.ipv4.ip_unprivileged_port_start: "0"
    labels:
      - "traefik.enable=true"
      # HTTP-to-HTTPS Redirect
      - "traefik.http.routers.http_catchall.rule=HostRegexp(`{any:.+}`)"
      - "traefik.http.routers.http_catchall.entrypoints=http"
      - "traefik.http.routers.http_catchall.middlewares=https_redirect@docker"
      - "traefik.http.middlewares.https_redirect.redirectscheme.scheme=https"
      - "traefik.http.middlewares.https_redirect.redirectscheme.permanent=true"
      # HTTP Routers
      - "traefik.http.routers.traefik-rtr.entrypoints=https"
      - "traefik.http.routers.traefik-rtr.rule=Host(`traefik.$DOMAINBASE`)"
      - "traefik.http.routers.traefik-rtr.tls=true"
      - "traefik.http.routers.traefik-rtr.tls.certresolver=ovh"
      - "traefik.http.routers.traefik-rtr.tls.domains[0].main=$DOMAINBASE"
      - "traefik.http.routers.traefik-rtr.tls.domains[0].sans=*.$DOMAINBASE"
      ## Middlewares
      - "traefik.http.routers.traefik-rtr.middlewares=traefik-headers@docker,rate-limit@file,oauth@file"
      - "traefik.http.middlewares.traefik-headers.headers.featurepolicy=camera 'none'; geolocation 'none'; microphone 'none'; payment 'none'; usb 'none'; vr 'none';"
      ## Services - API
      - "traefik.http.routers.traefik-rtr.service=api@internal"
      ## labels      
      - "com.centurylinklabs.watchtower.enable=false"        

adguard:
    container_name: adguardhome
    restart: unless-stopped
    image: adguard/adguardhome
    hostname: adguard
    ports:
      - "53:53"
      - "53:53/udp"
    expose:
      - "80"
      - "3000"
      - "443"
      - "853"
    volumes:
      - ${USERDIR}/adguard/conf:/opt/adguardhome/conf
      - ${USERDIR}/adguard/work:/opt/adguardhome/work
    user: ${PUID}
    networks:
      - reverseproxy
    labels:
      traefik.enable: "true"
      ## HTTP Routers
      traefik.http.routers.adguard-rtr.entrypoints: https
      traefik.http.routers.adguard-rtr.rule: Host(`dns.$DOMAINBASE`)
      traefik.http.routers.adguard-rtr.tls: "true"
      traefik.http.routers.adguard-rtr.tls.certresolver: ovh
      ## Middlewares
      ## HTTP Services
      traefik.http.routers.adguard-rtr.service: adguard-svc
      traefik.http.services.adguard-svc.loadbalancer.server.port: 80  
      ###TCP
      traefik.tcp.routers.adguard-tls.entrypoints: tls
      traefik.tcp.routers.adguard-tls.rule: HostSNI(`dns.$DOMAINBASE`)
      traefik.tcp.routers.adguard-tls.tls: "true"
      traefik.tcp.routers.adguard-tls.tls.certresolver: ovh
      ## Middlewares
      ## tcp Services
      traefik.tcp.routers.adguard-tls.service: adguard-svc-tls
      traefik.tcp.services.adguard-svc-tls.loadbalancer.server.port: 853

How to get this config working?

Akruidenberg commented 4 years ago

its working! wrong port: 853 is wrong. must be 53.

Akruidenberg commented 4 years ago

new update: DOT is sometimes slow with complex pages. DOH is working very well with traefik. I dont get this issue in the past (with self signed certifites and without traefik. Tried - "traefik.tcp.services.mytcpservice.loadbalancer.terminationdelay=100" but no luck. Is this a bug in Adguard?

tristanXme commented 3 years ago

I can't get DoT working with a custom signed Cert. Can i deactivate the cert validation from AdGuard Home?

tristanXme commented 3 years ago

/bump my question

ameshkov commented 3 years ago

Implemented long time ago. Closing since we don't plan to provide any UI setting for that, unencrypted DOH is an option that's supposed to be used by people who know what they're doing and know how to change the config file.

enquestor commented 3 years ago

Sorry for bumping this issue from long ago. I'm also trying to set up a nginx reverse proxy that manages the certificates and proxy to adguard container unencrypted.

This is my current tls part of the config:

tls:
  enabled: true
  server_name: my.domain.name
  force_https: false
  port_https: 0
  port_dns_over_tls: 853
  port_dns_over_quic: 784
  port_dnscrypt: 0
  dnscrypt_config_file: ""
  allow_unencrypted_doh: true
  strict_sni_check: false
  certificate_chain: ""
  private_key: ""
  certificate_path: ""
  private_key_path: ""

My main question is: How do I set the port DoH server is listening to? Cause I keep getting connection refused on port 80 when doing DoH queries with curl, even within the container.

ameshkov commented 3 years ago

@Allen-Hu port_https: 0 -- this is what you need.

enquestor commented 3 years ago

@ameshkov Yes I'm aware of that option, and already have it enabled (shown in my previous comment).

I'd like to know where (which port) I should proxy my DoH requests to, so I could config the reverse proxy correctly. Ex. http://adguardhome:80

Thanks in advance!

yegle commented 3 years ago

There is a bind_port option.

enquestor commented 3 years ago

@yegle I thought that was for the web interface only? Should I be sending my DoH requests there as well?

I set bind_port: 3000 and tried sending requests using curl within the reverse proxy container and get Bad Request from curl:

docker exec -it reverseproxy curl -H 'accept: application/dns-json' 'http://adguardhome:3000/dns-query?name=google.com&type=A'
Bad Request

while sending queries to other ports would result in connection refused:

docker exec -it reverseproxy curl -H 'accept: application/dns-json' 'http://adguardhome/dns-query?name=google.com&type=A'
curl: (7) Failed to connect to adguardhome port 80: Connection refused
yegle commented 3 years ago

Yes I set that to 80 and have my reverse proxy forward request to that and it seems to work.

https://github.com/yegle/your-dns/blob/master/adguard/conf/AdGuardHome.yaml.example this is the config I'm using.

yegle commented 3 years ago

Oh I see you are testing using the JSON API. I'm not actually sure Adguard Home supports that. I usually test a doh server using https://github.com/curl/doh.