> openvpn-dns-tools GitHub
for openvpn community edition & access server

A DNS query and a username, stitched back together.

OpenVPN sees IPs. It never tells you who ran a query. This installs three small services that capture DNS on the tunnel, track which user held which IP and when, and give you one command — vpn-lookup — to answer "who visited this, and when."

two independent logs, matched by ip + time
dns log 13:09:37 | client_ip=172.27.232.224 | domain=teams.live.com
session log 13:05:19 connect 172.27.232.224 ← annievillanueva
vpn-lookup --domain teams.live.com annievillanueva
one tool, two backends

Works the same either way

User attribution needs a source of truth for "who held this IP, when." Community Edition and Access Server expose that completely differently — dns-status-poller detects which one you're running and handles both.

Community Edition

Free, self-configured, server.conf you edit by hand.

  • Reads status.log — on by default in nearly every install script
  • Tunnel interface is usually tun0
  • No config edits or restart required

Access Server

Commercial product, managed through sacli and a web admin UI — including AWS Marketplace images.

  • Reads live state via sacli VPNStatus instead
  • Tunnel interfaces are named as0t0, as0t1, … — not tun0
  • Field mapping comes straight from AS's own header data — no guessing
before you run anything

Three things to check first

In order — each one avoids a dead end further down.

  1. You're root on the OpenVPN server itself. Not a client, not a jump box — the box actually running openvpn or Access Server. Every install and service step needs sudo or a root shell.
  2. OpenVPN is already installed and has active connections. This instruments an existing server — it doesn't set one up. Interface auto-detection also works better with at least one client connected during install.
  3. tshark can be installed. install.sh installs it via apt automatically if missing — just confirm the box has outbound access to your package mirror.
install

Two ways in

Both end up running the same install.sh — pick whichever fits how you work.

# pulls the repo, no git needed on the target box
curl -fsSL https://raw.githubusercontent.com/yousafkhamza/openvpn-dns-tools/main/bootstrap.sh | sudo bash
Note — this runs whatever's at that URL as root. Fine for your own repo. For anything beyond personal use, pin to a release tag instead of main, e.g. /raw/v1.0.0/bootstrap.sh.
git clone https://github.com/yousafkhamza/openvpn-dns-tools.git
cd openvpn-dns-tools
sudo ./install.sh
Note — use this if you want to read install.sh before running it, or you're on a box with no outbound access to raw.githubusercontent.com.

install.sh auto-detects your tunnel interface, your attribution backend, and installs everything — including short commands on your $PATH. Enable the services when it tells you to:

sudo systemctl enable --now openvpn-dns-capture.service
sudo systemctl enable --now openvpn-dns-status-poller.service
sudo systemctl enable --now openvpn-dns-cleanup.timer
where your data lives

Every log, one table

Nothing hidden — these are the only paths this tool writes to.

PathWhat's in it
/var/log/openvpn-dns/openvpn-dns-YYYY-MM-DD.log One file per day. Every DNS query seen on the tunnel: timestamp, client IP, domain, record type. Deleted automatically after 2 days.
/var/log/openvpn-dns.log Symlink to today's file above — a fixed path for anything that expects one.
/var/log/openvpn-dns/openvpn-sessions.log Who held which VPN IP, and when — connect/disconnect events from dns-status-poller (or the optional hooks). This is what makes vpn-lookup able to name a user, not just an IP. Rotated monthly, kept 12 months.
/etc/systemd/system/openvpn-dns-* The three services/timer: capture, status-poller, cleanup.
/etc/logrotate.d/openvpn-dns-sessions Rotation rule for the session log — installed automatically.
after install

Commands, from anywhere

Installed to /usr/local/bin — no path to type, no .py. Run the ones marked root as sudo — anything else just tells you it needs root rather than failing cryptically.

CommandDoesNeeds root?
vpn-lookup --domain loadway Search by domain, IP, user, or time range usually not
dns-capture -i tun0 Runs as the systemd service — sniffs DNS on the tunnel root
dns-status-poller Runs as the systemd service — tracks IP↔user over time root
dns-cleanup Deletes DNS logs older than 2 days root