DnsDist · Nginx · DoH · DoT · UDP

SmartDNS proxy to hide your geo-location

SniDust proxies your DNS requests and spoofs your origin IP, so geo-restricted services (Zattoo, Netflix, SRF.ch, Yallo, Hulu, Amazon Prime, …) see the IP of your server instead of yours.

How it works

Client device 10.111.123.7 SniDust server 10.111.123.8 ↑ Google · Cloudflare CDN edge 104.98.130.197 1 DNS question: hulu.com 2 answer: hulu.com ⇒ 10.111.123.8 3 HTTPS request to hulu.com 4 proxied on to the edge IP 5 video stream back 6 stream on to the client
  1. 10.111.123.7 → 10.111.123.8DNS question for hulu.com
  2. 10.111.123.8 → 10.111.123.7answer: hulu.com ⇒ 10.111.123.8
  3. 10.111.123.7 → 10.111.123.8HTTPS request to hulu.com
  4. 10.111.123.8 → 104.98.130.197proxied request to the CDN edge
  5. 104.98.130.197 → 10.111.123.8video stream back
  6. 10.111.123.8 → 10.111.123.7stream on to the client

1 · Access control

Only clients on the ACL may use the proxy. IPs and DynDNS domains from ALLOWED_CLIENTS are resolved to IPs and written to /etc/dnsdist/allowedClients.acl; a cron re-resolves DynDNS names every 15 min.

2 · Blocklist match

Queries whose suffix matches one of the 11 lists in /etc/snidust/domains.d (55 entries) are tagged; everything else is passed through untouched.

3 · Spoofed answer

Tagged queries never reach the real CDN. SpoofAction(EXTERNAL_IP) rewrites the record itself, so the client is told that hulu.com resolves to the server — and opens its video connection there.

4 · Reverse proxy

nginx proxies that connection to the real host (proxy_pass http://$host$request_uri), resolving the name from the server's own view — the geo-correct CDN edge. The stream is piped back to the client.

Supported services

Optimised for these services; the shipped lists cover their CDN and API domains.

Zattoo

ch + wemfbox + akamai variants

Netflix

nflx* + netflix.de

SRF / RTS

srgssr.ch, cdn.rts.ch

Yallo

yallo.tv, zahs.tv

Hulu

hulu.com, huluim.com

Amazon Prime

amazonvideo.com, amazon.co.uk

HBO

hbogo.com, hbonow.com

Molotov TV

molotov.tv, y3o.tv

YouTube

youtubei, googlevideo, youtu.be

Akamai CDN

10 akamai domains

Blocklist fileEntries
00-debug.lst1
01-cdn_akamai.lst11
02-amazon.lst3
03-hbo.lst3
04-hulu.lst2
05-netflix.lst11
06-molotov_tv.lst2
07-srf_ch.lst4
09-zattoo.lst9
10-yallo.lst2
11-youtube.lst7
Total55

Quick start

  • Find your client IP
    curl https://ifconfig.co

    Run this on the device that will use the proxy. DynDNS domains work as well.

  • Find your server IP
    curl https://ifconfig.co

    Run this on the VPS that hosts SniDust. This is the IP that gets spoofed.

  • Start the image
    docker run -d \
      --name snidust \
      -e ALLOWED_CLIENTS="127.0.0.1, 10.111.123.7, myDynDNSDomain.no-ip.com" \
      -e EXTERNAL_IP=10.111.123.8 \
      -p 443:8443 -p 80:8080 -p 53:5300/udp \
      ghcr.io/seji64/snidust:latest

    Prebuilt for linux/amd64, linux/arm64 and linux/arm/v7 — no local build needed.

  • Check the log
    docker logs snidust

    You should see the upstreams (dns.google, cloudflare-dns) marked up and the ACL loaded.

  • Point your device at the server
    sudo resolvctl set nameservers 10.111.123.8   # macOS
    # or set DoH/DoT manually: https://10.111.123.8/dns-query

    Set the DNS server of the device (or the whole network) to the public IP of your server. Geo-location hidden.

  • Docker Compose

    version: '3.3'
    services:
        snidust:
            container_name: snidust
            image: 'ghcr.io/seji64/snidust:latest'
            environment:
                - TZ=Europe/Berlin
                - 'ALLOWED_CLIENTS=127.0.0.1, 10.111.123.7, myDynDNSDomain.no-ip.com'
                - 'EXTERNAL_IP=10.111.123.8'
                - SPOOF_ALL_DOMAINS=false
                # - 'DYNDNS_CRON_SCHEDULE=*/1 * * * *'
            ports:
                - 443:8443
                - 80:8080
                - 53:5300/udp

    Ports

    PortProtocolPurpose
    5300/udpDNSClassic DNS endpoint — map to 53:5300/udp.
    8080/tcpDoH (plain)Nginx reverse proxy for DNS-over-HTTPS, map to 80:8080.
    8443/tcpDoH / DoT (TLS)Nginx stream module with ssl_preread, map to 443:8443.
    8530/tcpDoH / DoT (TLS)Native dnsdist TLS endpoint, only with DNSDIST_ENABLE_DOT=true.
    8083/tcpHTTPdnsdist web interface, bound to 127.0.0.1; needs DNSDIST_ENABLE_WEBSERVER=true and -p 8083:8083.

    Configuration

    Core settings

    VariableDefaultDescription
    ALLOWED_CLIENTS127.0.0.1Comma-separated IPs or DynDNS domains allowed to use the proxy.
    ALLOWED_CLIENTS_FILE(empty)Path to an ACL file; allows reloading without a restart.
    EXTERNAL_IP(empty)Public server IP used for spoofing.
    SPOOF_ALL_DOMAINSfalseSpoof every query, regardless of the blocklists (not recommended).
    INSTALL_DEFAULT_DOMAINStrueInstall the domain lists shipped in the repo.
    DYNDNS_CRON_SCHEDULE*/15 * * * *Cron schedule for re-resolving DynDNS domains.

    DnsDist performance & security

    VariableDefaultDescription
    DNSDIST_RATE_LIMIT_DISABLEfalseDisable rate limiting entirely.
    DNSDIST_RATE_LIMIT_WARN800Warn above this many queries per second.
    DNSDIST_RATE_LIMIT_BLOCK1000Block above this many queries per second.
    DNSDIST_RATE_LIMIT_BLOCK_DURATION360Seconds a client stays blocked.
    DNSDIST_RATE_LIMIT_EVAL_WINDOW60Evaluation window in seconds.
    DNSDIST_PACKAGE_CACHE_ENABLEDfalseEnable packet cache to cut latency.
    DNSDIST_PACKAGE_CACHE_SIZE50000Maximum entries in the packet cache.
    DNSDIST_UPSTREAM_POOL_NAMEupstreamPool name; change when using custom upstreams.
    DNSDIST_UPSTREAM_CHECK_INTERVAL10Seconds between upstream health checks.
    DNSDIST_ENABLE_DOTfalseEnable native DoT/DoH on port 8530.
    DNSDIST_DOT_CERT_TYPEself-signedCertificate type for DoT (self-signed / ACME).
    DNSDIST_ENABLE_WEBSERVERfalseEnable the dnsdist web UI on 127.0.0.1:8083.
    DNSDIST_DEBUGfalseLog every query to /tmp/dnsdist.log.

    Advanced setups

    DNS over TLS (DoT)

    See docker-compose.dot.yml and docker-compose.acme.sh-dot.yml in the repo for a self-signed or an ACME (real certificate) setup.

    Custom upstreams

    Create 99-customUpstream.conf with newServer({address="…", pool="customUpstream"}), mount it into /etc/dnsdist/conf.d/ and set DNSDIST_UPSTREAM_POOL_NAME=customUpstream.

    Custom domain lists

    Add 99-custom.lst and mount it into /etc/snidust/domains.d/99-custom.lst:ro — one domain per line.

    Custom nginx config

    Mount your own file at /etc/nginx/nginx.conf:ro for reverse-proxy or performance tuning.

    Reload without restarting

    SniDust answers specific magic queries and re-reads its configuration in place — no restart, no downtime.

    dig @10.111.123.8 reload.acl.snidust.local         # re-read the ACL
    dig @10.111.123.8 reload.domainlist.snidust.local  # re-read the blocklists
    dig @10.111.123.8 reload.certs.snidust.local       # re-read the DoT certificates

    Troubleshooting

    Port 53 already in use. On Linux, systemd-resolved (or Pi-hole) usually occupies port 53. Free it before mapping 53:5300/udp — see the guide linked from the README.

    Nothing is spoofed? Check the domain is really covered by a list (docker exec snidust cat /etc/snidust/domains.d/*.lst), that your client IP is in the ACL (docker exec snidust cat /etc/dnsdist/allowedClients.acl), and set DNSDIST_DEBUG=true to watch queries in /tmp/dnsdist.log.

    Requirements: a VPS or root server with Docker, a public IP that is not itself geo-blocked, and a client whose DNS you can change. The image is Alpine-based, runs as snidust and is supervised by tini.

    Credits

    Built on dnsdist.org, nginx.org, wilmaa-proxy, unblock-proxy.sh and netflix-proxy.

    Source: github.com/Seji64/SniDust · Image: ghcr.io/seji64/snidust:latest