From dc2b066fe3105500e571ecf25f51ef57b5bf8c45 Mon Sep 17 00:00:00 2001 From: Nico D'Cotta <45274424+Cottand@users.noreply.github.com> Date: Mon, 13 Nov 2023 21:56:03 -0100 Subject: [PATCH] docs: produce website with mkbook (#32) --- .github/workflows/docs.yml | 56 +++++++ doc/.gitignore | 1 + doc/book.toml | 6 + doc/src/Blocking.md | 5 + "doc/src/CNAME\342\200\220following-DNS.md" | 58 ++++++++ doc/src/Configuration.md | 104 +++++++++++++ doc/src/Custom-DNS-Records.md | 10 ++ doc/src/DNS-over-HTTPS-(DoH).md | 35 +++++ doc/src/Deploying-on-Debian.md | 60 ++++++++ doc/src/Home.md | 1 + doc/src/Init-Scripts.md | 153 ++++++++++++++++++++ doc/src/Nix.md | 28 ++++ doc/src/Prometheus-Metrics.md | 8 + doc/src/SUMMARY.md | 22 +++ doc/src/Securing-on-linux.md | 23 +++ flake.nix | 4 +- 16 files changed, 572 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/docs.yml create mode 100644 doc/.gitignore create mode 100644 doc/book.toml create mode 100644 doc/src/Blocking.md create mode 100644 "doc/src/CNAME\342\200\220following-DNS.md" create mode 100644 doc/src/Configuration.md create mode 100644 doc/src/Custom-DNS-Records.md create mode 100644 doc/src/DNS-over-HTTPS-(DoH).md create mode 100644 doc/src/Deploying-on-Debian.md create mode 100644 doc/src/Home.md create mode 100644 doc/src/Init-Scripts.md create mode 100644 doc/src/Nix.md create mode 100644 doc/src/Prometheus-Metrics.md create mode 100644 doc/src/SUMMARY.md create mode 100644 doc/src/Securing-on-linux.md diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..224038a --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,56 @@ +name: Deploy mdBook site to Pages + +on: + # Runs on pushes targeting the default branch + push: + branches: ["master"] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + # Build job + build: + runs-on: ubuntu-latest + env: + MDBOOK_VERSION: 0.4.21 + steps: + - uses: actions/checkout@v3 + - name: Install mdBook + run: | + curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf -y | sh + rustup update + cargo install --version ${MDBOOK_VERSION} mdbook + - name: Setup Pages + id: pages + uses: actions/configure-pages@v3 + - name: Build with mdBook + run: mdbook build + - name: Upload artifact + uses: actions/upload-pages-artifact@v2 + with: + path: ./doc/book + + # Deployment job + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v2 diff --git a/doc/.gitignore b/doc/.gitignore new file mode 100644 index 0000000..7585238 --- /dev/null +++ b/doc/.gitignore @@ -0,0 +1 @@ +book diff --git a/doc/book.toml b/doc/book.toml new file mode 100644 index 0000000..8a4de93 --- /dev/null +++ b/doc/book.toml @@ -0,0 +1,6 @@ +[book] +authors = ["Cottand"] +language = "en" +multilingual = false +src = "src" +title = "leng-doc" diff --git a/doc/src/Blocking.md b/doc/src/Blocking.md new file mode 100644 index 0000000..721cbb1 --- /dev/null +++ b/doc/src/Blocking.md @@ -0,0 +1,5 @@ +There are many blocklist resources online, and by default leng is configured to use some of the more popular ones from around the internet for blocking ads and malware domains. Some services exist that will allow you to regularly get blocklist updates automatically from feeds. + +## Blocklists + +[https://github.com/StevenBlack/hosts/](https://github.com/StevenBlack/hosts/) diff --git "a/doc/src/CNAME\342\200\220following-DNS.md" "b/doc/src/CNAME\342\200\220following-DNS.md" new file mode 100644 index 0000000..ae31cad --- /dev/null +++ "b/doc/src/CNAME\342\200\220following-DNS.md" @@ -0,0 +1,58 @@ +Leng implements following CNAME records as specified in [RFC-1034 section 3.6.2](https://www.rfc-editor.org/rfc/rfc1034#section-3.6.2), where it returns all necessary CNAME and A records to fully resolve the query (as opposed to just returning a synthetic A record, which is known as [CNAME flattening](https://developers.cloudflare.com/dns/cname-flattening/cname-flattening-diagram/)). + +> ⚠ This is the behaviour of most if not all DNS servers - Leng is only special in this in that it has to deal with cuustom DNS records, the resolvers it proxies, and blocklists. **You should not need to change its default behaviour**, but this page aims to leave it well-documented. + +## `dig` request example + +```bash +$> dig first.example + +; <<>> DiG 9.18.19 <<>> first.example + +;; QUESTION SECTION: +;first.example. IN A + +;; ANSWER SECTION: +first.example. 300 IN CNAME second.example. +second.example. 300 IN CNAME third.example. +third.example. 300 IN A 139.201.133.245 +``` + +## Behaviour + +The resolving for the downstream CNAME records is done with the same question type as the original question. That is, if you ask `AAAA some-cname.com`, the following CNAME queries will be `AAAA` questions too. + +### Custom records + +If you have set up your own custom records, those can also be part of the CNAME chain. + +This makes it easy to alias custom records to external domains: +``` +customdnsrecords = [ + "login.vpn IN CNAME this.very.long.other.domain.login.login.my-company.xyz" +] +``` + +Querying `login.vpn` will also return the A record corresponding to `this.very.long.other.domain.login.login.my-company.xyz`. + +### Blocking +If any of the domains involved in the CNAME-following is part of a blocklist (that is, it would get blocked if it corresponded to an `A` response, rather than `CNAME`) then the entire request blocked (_unless_ the domain is part of the custom DNS defined in the config) + +For the example where we have +``` +first.example IN CNAME second.example +second.example IN CNAME third.example +third.example IN A 10.0.0.0 +``` + +if any of `first.example`, `second.example` or `third.example` appear in a blocklist, the request for `first.example` will fail. + +## Configuration + +CNAME-following is enabled by default, but you can disabled with the following: + +```toml +# leng.toml + +followCnameDepth = 0 +``` \ No newline at end of file diff --git a/doc/src/Configuration.md b/doc/src/Configuration.md new file mode 100644 index 0000000..21c19ab --- /dev/null +++ b/doc/src/Configuration.md @@ -0,0 +1,104 @@ +If leng.toml is not found the default configuration will be used. If it is found, fields that are set will act as overrides. + +Here is the default configuration: + +```toml +# list of sources to pull blocklists from, stores them in ./sources +sources = [ + "https://mirror1.malwaredomains.com/files/justdomains", + "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts", + "https://sysctl.org/cameleon/hosts", + "https://s3.amazonaws.com/lists.disconnect.me/simple_tracking.txt", + "https://s3.amazonaws.com/lists.disconnect.me/simple_ad.txt", + "https://gitlab.com/quidsup/notrack-blocklists/raw/master/notrack-blocklist.txt" +] + +# list of locations to recursively read blocklists from (warning, every file found is assumed to be a hosts-file or domain list) +sourcedirs = [ + "sources" +] + +# log configuration +# format: comma separated list of options, where options is one of +# file:@ +# stderr>@ +# syslog@ +# loglevel: 0 = errors and important operations, 1 = dns queries, 2 = debug +# e.g. logconfig = "file:leng.log@2,syslog@1,stderr@2" +logconfig = "stderr@2" + +# apidebug enables the debug mode of the http api library +apidebug = false + +# address to bind to for the DNS server +bind = "0.0.0.0:53" + +# address to bind to for the API server +api = "127.0.0.1:8080" + +# response to blocked queries with a NXDOMAIN +nxdomain = false + +# ipv4 address to forward blocked queries to +nullroute = "0.0.0.0" + +# ipv6 address to forward blocked queries to +nullroutev6 = "0:0:0:0:0:0:0:0" + +# nameservers to forward queries to +nameservers = ["1.1.1.1:53", "1.0.0.1:53"] + +# concurrency interval for lookups in miliseconds +interval = 200 + +# query timeout for dns lookups in seconds +timeout = 5 + +# cache entry lifespan in seconds +expire = 600 + +# cache capacity, 0 for infinite +maxcount = 0 + +# question cache capacity, 0 for infinite but not recommended (this is used for storing logs) +questioncachecap = 5000 + +# manual blocklist entries +blocklist = [] + +# Drbl related settings +usedrbl = 0 +drblpeersfilename = "drblpeers.yaml" +drblblockweight = 128 +drbltimeout = 30 +drbldebug = 0 + +# manual whitelist entries - comments for reference +whitelist = [ + # "getsentry.com", + # "www.getsentry.com" +] + +# manual custom dns entries - comments for reference +customdnsrecords = [ + # "example.mywebsite.tld IN A 10.0.0.1" + # "example.other.tld IN CNAME wikipedia.org" +] + +# When this string is queried, toggle leng on and off +togglename = "" + +# If not zero, the delay in seconds before leng automaticall reactivates after +# having been turned off. +reactivationdelay = 300 + +#Dns over HTTPS provider to use. +DoH = "https://cloudflare-dns.com/dns-query" + +# Prometheus metrics - enable +[Metrics] + enabled = false + path = "/metrics" +``` + +The most up-to-date version can be found on [config.go](https://github.com/Cottand/leng/blob/master/config.go) \ No newline at end of file diff --git a/doc/src/Custom-DNS-Records.md b/doc/src/Custom-DNS-Records.md new file mode 100644 index 0000000..a908b57 --- /dev/null +++ b/doc/src/Custom-DNS-Records.md @@ -0,0 +1,10 @@ +You can make leng return records of your choosing (which will take precedence over upstream DNS records) by setting `customdnsrecords` in the [[Configuration]]. + +Custom DNS records are represented as [Resource Record](https://en.wikipedia.org/wiki/Domain_Name_System#Resource_records) strings. Class defaults to IN and TTL defaults to 3600. Full zone file syntax is supported. + +```toml +customdnsrecords = [ + "example.com. 3600 IN A 10.10.0.1", + "example.cname.com. IN CNAME wikipedia.org", +] +``` \ No newline at end of file diff --git a/doc/src/DNS-over-HTTPS-(DoH).md b/doc/src/DNS-over-HTTPS-(DoH).md new file mode 100644 index 0000000..b65886f --- /dev/null +++ b/doc/src/DNS-over-HTTPS-(DoH).md @@ -0,0 +1,35 @@ +# DNS-over-HTTP(S), aka DoH + +Leng supports DNS-over-HTTPS as per [RFC-8484](https://datatracker.ietf.org/doc/html/rfc8484), although it is disabled by default. + +Custom DNS records will be served over DoH the same as normal DNS requests. + +You can specify your key files yourself to have leng serve HTTPS traffic, or you can let leng serve HTTP traffic and have a proxy manage the HTTPS certificates. + +### Specifying Key files (HTTP) + +```toml +[DnsOverHttpServer] + enabled = true + bind = "0.0.0.0:80" + timeoutMs = 5000 + + [DnsOverHttpServer.TLS] + enabled = true + certPath = "" + keyPath = "" + # if empty, system CAs will be used + caPath = "" +``` + +### Not specifying key files (TLS disabled, HTTP traffic from leng) +```toml +[DnsOverHttpServer] + enabled = true + bind = "0.0.0.0:80" + timeoutMs = 5000 +``` + +> ⚠ It is not recommended to use HTTP without TLS at all directly. Your queries will be un-encrypted, so they won't be much different than normal UDP queries. + +You can use DoH [in most browsers](https://ghacks.net/2021/10/23/how-to-enable-dns-over-https-secure-dns-in-chrome-brave-edge-firefox-and-other-browsers/). \ No newline at end of file diff --git a/doc/src/Deploying-on-Debian.md b/doc/src/Deploying-on-Debian.md new file mode 100644 index 0000000..68ac5ca --- /dev/null +++ b/doc/src/Deploying-on-Debian.md @@ -0,0 +1,60 @@ +# Deploying on Debian + +## Installing leng + +Installing leng is the easiest when you simply download a release from the [releases](https://github.com/looterz/leng/releases) page. Go ahead and copy the link for leng_linux_x64 and run the following in your terminal. + +``` +mkdir ~/grim +cd ~/grim +wget +``` + +This will download the binary to ```~/grim``` which will be leng's working directory. First, lets setup file permissions for leng, by running the following. + +``` +chmod a+x ./leng_linux_x64 +``` + +Setup is pretty much complete, the only thing left to do is run leng and let it generate the default configuration and download the blocklists, but lets set it up as a systemd service so it automatically restarts and updates when starting. + +## Setting up the service + +Create the leng service by running the following, + +``` +nano /etc/systemd/system/leng.service +``` + +Now paste in the code for the service below, + +``` +[Unit] +Description=leng dns proxy +Documentation=https://github.com/looterz/leng +After=network.target + +[Service] +User=root +WorkingDirectory=/root/grim +LimitNOFILE=4096 +PIDFile=/var/run/leng/leng.pid +ExecStart=/root/grim/leng_linux_x64 -update +Restart=always +StartLimitInterval=30 + +[Install] +WantedBy=multi-user.target +``` + +Save, and now you can start, stop, restart and run status commands on the leng service like follows +``` +systemctl start leng # start +systemctl enable leng # start on boot +``` + +The only thing left to do is setup your clients to use your leng dns server. + +## Security + +Now that leng is setup on your droplet, it's recommended to [secure](https://github.com/looterz/leng/wiki/Securing-on-linux) the installation from non-whitelisted clients. \ No newline at end of file diff --git a/doc/src/Home.md b/doc/src/Home.md new file mode 100644 index 0000000..92b00e4 --- /dev/null +++ b/doc/src/Home.md @@ -0,0 +1 @@ +Here are some useful guides and resources for working with leng. Contributions welcome! \ No newline at end of file diff --git a/doc/src/Init-Scripts.md b/doc/src/Init-Scripts.md new file mode 100644 index 0000000..8c35bcf --- /dev/null +++ b/doc/src/Init-Scripts.md @@ -0,0 +1,153 @@ +Some parameters may require your attention, such as path and executable name. + +## systemd +Create ```/etc/systemd/services/leng.service``` and paste in the following. + +``` +[Unit] +Description=leng dns proxy +Documentation=https://github.com/looterz/leng +After=network.target + +[Service] +User=root +WorkingDirectory=/root/grim +LimitNOFILE=4096 +PIDFile=/var/run/leng/leng.pid +ExecStart=/root/grim/leng_linux_x64 -update +Restart=always +StartLimitInterval=30 + +[Install] +WantedBy=multi-user.target +``` + +## init.d +Create ```/etc/init.d/leng``` and paste in the following. + +```bash +#!/bin/bash +# leng daemon +# chkconfig: 345 20 80 +# description: leng daemon +# processname: leng + +DAEMON_PATH="/root/grim" + +DAEMON=leng +DAEMONOPTS="-update" + +NAME=leng +DESC="https://github.com/looterz/leng" +PIDFILE=/var/run/$NAME.pid +SCRIPTNAME=/etc/init.d/$NAME + +case "$1" in +start) + printf "%-50s" "Starting $NAME..." + cd $DAEMON_PATH + PID=`$DAEMON $DAEMONOPTS > /dev/null 2>&1 & echo $!` + #echo "Saving PID" $PID " to " $PIDFILE + if [ -z $PID ]; then + printf "%s\n" "Fail" + else + echo $PID > $PIDFILE + printf "%s\n" "Ok" + fi +;; +status) + printf "%-50s" "Checking $NAME..." + if [ -f $PIDFILE ]; then + PID=`cat $PIDFILE` + if [ -z "`ps axf | grep ${PID} | grep -v grep`" ]; then + printf "%s\n" "Process dead but pidfile exists" + else + echo "Running" + fi + else + printf "%s\n" "Service not running" + fi +;; +stop) + printf "%-50s" "Stopping $NAME" + PID=`cat $PIDFILE` + cd $DAEMON_PATH + if [ -f $PIDFILE ]; then + kill -HUP $PID + printf "%s\n" "Ok" + rm -f $PIDFILE + else + printf "%s\n" "pidfile not found" + fi +;; + +restart) + $0 stop + $0 start +;; + +*) + echo "Usage: $0 {status|start|stop|restart}" + exit 1 +esac +``` + +## rc.d +Create ```/etc/rc.d/leng``` and paste in the following. + +```sh +#!/bin/sh +# +# $FreeBSD$ +# +# PROVIDE: leng +# REQUIRE: NETWORKING SYSLOG +# KEYWORD: shutdown +# +# Add the following lines to /etc/rc.conf to enable leng: +# +#leng_enable="YES" + +. /etc/rc.subr + +name="leng" +rcvar="leng_enable" + +load_rc_config $name + +: ${leng_user:="root"} +: ${leng_enable:="NO"} +: ${leng_directory:="/root/grim"} + +command="${leng_directory}/leng -update" + +pidfile="${leng_directory}/${name}.pid" + +start_cmd="export USER=${leng_user}; export HOME=${leng_directory}; /usr/sbin/daemon -f -u ${leng_user} -p ${pidfile} $command" + +#stop_cmd="kill $(cat $pidfile)" +stop_cmd="${name}_stop" +leng_stop() { + if [ ! -f $pidfile ]; then + echo "leng PID File not found. Maybe leng is not running?" + else + kill $(cat $pidfile) + fi +} + +run_rc_command "$1" +``` +or +```#!/bin/sh +# +# OpenBSD +# +daemon="" + +. /etc/rc.d/rc.subr + +rc_bg=YES +rc_reload=NO + +rc_cmd $1 +``` \ No newline at end of file diff --git a/doc/src/Nix.md b/doc/src/Nix.md new file mode 100644 index 0000000..107bb82 --- /dev/null +++ b/doc/src/Nix.md @@ -0,0 +1,28 @@ +# Nix + +Leng is also packaged as [a Nix flake](../../flake.nix). + +## Running + +You can simply run `nix run github:cottand/leng` to run latest `master`. + +## Installing + +### In your flake + +```nix +{ + # pinned version for safety + inputs.grimn.url = "github:cottand/leng/v1.3.1"; + + outputs = { self, leng }: { + # Use in your outputs + }; +} +``` + + +## Developing + +The flake's development shell simply includes Go 1.21+ and a [fish](https://fishshell.com/) shell. You can enter it with `nix develop`. + diff --git a/doc/src/Prometheus-Metrics.md b/doc/src/Prometheus-Metrics.md new file mode 100644 index 0000000..6bbdcfc --- /dev/null +++ b/doc/src/Prometheus-Metrics.md @@ -0,0 +1,8 @@ +The HTTP API has a `/metrics` endpoint that exposes Go runtime metrics as well as things including: +- downstream DNS requests, broken down by type +- upstream DNS requests +- upstream DNS-over-HTTPS success rate +- downstream DNS-over-HTTPS success rate + + +No grafana dashboards exist for leng yet. If you make one, please make a PR! \ No newline at end of file diff --git a/doc/src/SUMMARY.md b/doc/src/SUMMARY.md new file mode 100644 index 0000000..09dc1ba --- /dev/null +++ b/doc/src/SUMMARY.md @@ -0,0 +1,22 @@ +# Summary + +- [Home](./Home.md) + +# DNS + +- [DNS-over-HTTP](./DNS-over-HTTPS-(DoH).md) +- [Custom DNS Records](Custom-DNS-Records.md) +- [CNAME following](CNAME‐following-DNS.md) +- [Blocking DNS](Blocking.md) + +# Administration + +- [Configuration](./Configuration.md) +- [Prometheus Metrics](./Prometheus-Metrics.md) + +# Deployment + +- [As systemd service on Debian-based distro](./Deploying-on-Debian.md) +- [Securing with a Firewall on Linux](./Securing-on-linux.md) +- [Nix](./Nix.md) +- [Init Scripts & Configs](./Init-Scripts.md) diff --git a/doc/src/Securing-on-linux.md b/doc/src/Securing-on-linux.md new file mode 100644 index 0000000..eab3033 --- /dev/null +++ b/doc/src/Securing-on-linux.md @@ -0,0 +1,23 @@ +# Securing on Linux + +The recommended way to harden access to the leng server is to only allow connections from clients you trust, mainly because public dns servers are hit by penetration testers and hackers regularly to scout for vulnerabilities. + +## Installing Requirements + +Let's grab ufw to allow for easy editing of iptables. + +``` +apt-get install ufw -y +``` + +## Firewall Setup + +Now let's whitelist our dns clients IP address or range, and block access from everywhere else by default using ufw. + +``` +ufw deny 53 +ufw allow from to any port 53 +ufw reload +``` + +Now only the client(s) you whitelisted can access the dns server. \ No newline at end of file diff --git a/flake.nix b/flake.nix index ec1840d..2292cd2 100644 --- a/flake.nix +++ b/flake.nix @@ -26,8 +26,8 @@ # Dev environment ## use with `nix develop` devShells = rec { - leng = pkgs.mkShell { - packages = [ pkgs.fish pkgs.go_1_21 ]; + leng = with pkgs; mkShell { + packages = [ fish go_1_21 mdbook ]; # Note that `shellHook` still uses bash syntax. This starts fish, then exists the bash shell when fish exits. shellHook = "fish && exit"; };