Deployment guide

whois-api-server

Overview

whois-api-server (the open source KincaidYang/whois) is a self-hosted WHOIS and RDAP query API. It looks up domains, IPv4 and IPv6 addresses, CIDR prefixes and AS numbers against the live WHOIS and RDAP registries on demand. It parses the reply into clean, structured RDAP-vocabulary JSON and caches it. A WHOIS proxy answers from the registries themselves, so the image queries the public WHOIS (TCP 43) and RDAP (HTTPS 443) servers over the outbound network. There is no dataset to ship or keep up to date.

The server is a single self-contained native Go binary that runs behind nginx as a reverse proxy on port 80. SecureAnyCloud also bundles a self-contained lookup web UI, served locally by nginx, that runs a query in the browser and renders the parsed registry data with no external CDN.

What you get:

  • whois-api-server 1.2.0 (single native Go binary) at /opt/whois-api-server/whois-server. The API listens on 127.0.0.1:8043. The upstream binary binds every interface, so the image adds an nftables rule that fences the app port to the loopback interface and leaves nginx as the only public surface.
  • An nginx reverse proxy on :80 in front of the API on loopback :8043, with a strict path allowlist, version disclosure suppressed and per-IP rate limiting on the lookup path.
  • A self-contained SecureAnyCloud lookup web UI served at / (vanilla HTML/JS, no external CDN) that queries the API and renders the result.
  • In-memory caching with no Redis dependency by default, so repeated queries do not re-hit upstream registries.
  • whois-api-server.service and nginx.service as systemd units, enabled and active. A whois-api-server-firstboot.service one-shot runs once per VM, and an nftables.service loopback fence protects the app port.
  • A dedicated non-root whois service account for the API, with no shell and no sudo.
  • A fully patched Ubuntu 24.04 LTS security baseline at capture time, with unattended security updates enabled.
  • 24/7 SecureAnyCloud support.

whois-api-server is a public-by-design lookup service. The WHOIS/RDAP lookup and the web UI need no login, because there is no default credential to leak, so none ships in the image or is generated at first boot.

On the first boot of every deployed VM, a one-shot service resolves the VM public address, writes an instance info file to /root/whois-api-server-info.txt, records the bootstrap-ready marker, and starts the API and the front door. The API unit is gated on that marker, so it never serves before first boot has completed.

Key facts: platform Ubuntu 24.04 LTS on Azure, default SSH user azureuser, binary /opt/whois-api-server/whois-server, config /etc/whois/config.yaml, web UI and lookup API on port 80, API loopback on 8043.

Prerequisites

  • An active Azure subscription, an SSH key pair, and a VNet and subnet in the target region.
  • Standard_B2s (2 vCPU / 4 GiB RAM) is a good starting point. The service is a small Go binary with an in-memory cache, so it needs little memory.
  • NSG inbound rules that allow 22/tcp from your management network, and 80/tcp from the networks that will consume the API. Add 443/tcp once you place your own TLS terminator in front.
  • Outbound internet access, so the VM can reach the upstream WHOIS (43/tcp) and RDAP (443/tcp) servers.

Step 1: Deploy from the Azure Marketplace

Find the SecureAnyCloud whois-api-server on Ubuntu 24.04 LTS offer in Azure Marketplace, then select Get It Now and Create. Choose your subscription, resource group and region, pick Standard_B2s, and provide your SSH public key for the azureuser account.

On the Networking tab, attach the VM to your VNet and subnet and to a network security group. The group must allow inbound 22/tcp from your management network and 80/tcp from your API consumers. Then review and create.

Step 2: Deploy from the Azure CLI

You can deploy the same image from the CLI. Create the network, then create the VM from the SecureAnyCloud image plan. Replace the example names to suit your environment.

az group create --name whois-rg --location uksouth
az network vnet create --resource-group whois-rg --name whois-vnet \
  --address-prefix 10.20.0.0/16 --subnet-name whois-subnet --subnet-prefix 10.20.1.0/24
az network nsg create --resource-group whois-rg --name whois-nsg
az network nsg rule create --resource-group whois-rg --nsg-name whois-nsg \
  --name allow-ssh --priority 1000 --destination-port-ranges 22 --access Allow --protocol Tcp
az network nsg rule create --resource-group whois-rg --nsg-name whois-nsg \
  --name allow-http --priority 1010 --destination-port-ranges 80 --access Allow --protocol Tcp
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:whois-api-server-ubuntu-24-04:default:latest
az vm create --resource-group whois-rg --name whois-vm \
  --image sac1647283583153:whois-api-server-ubuntu-24-04:default:latest \
  --size Standard_B2s --admin-username azureuser \
  --ssh-key-values ~/.ssh/id_rsa.pub \
  --vnet-name whois-vnet --subnet whois-subnet --nsg whois-nsg --public-ip-sku Standard

Step 3: Connect to your VM

ssh azureuser@<vm-ip>

The first-boot service runs automatically. Its message-of-the-day banner and /root/whois-api-server-info.txt show the URLs for this VM.

Step 4: Verify the stack

Confirm that the two services are active and that nginx is public on :80 while the API is on :8043. Confirm also that the nftables rule fences the app port to loopback.

systemctl is-active whois-api-server nginx whois-api-server-firstboot ss -tln | grep -E ':80 |:8043 '
sudo nft list ruleset | grep -A4 'table inet whois_filter'

The expected output is:

active active active LISTEN 0 511 0.0.0.0:80 0.0.0.0:* LISTEN 0 4096 *:8043 *:* LISTEN 0 511 [::]:80 [::]:* table inet whois_filter { chain input { type filter hook input priority filter; policy accept; iif "lo" accept ct state established,related accept tcp dport 8043 drop

The API binds :8043 on all interfaces. The tcp dport 8043 drop rule applies to anything not arriving on lo, so the API is reachable only through nginx or from on-box localhost.

The health probe confirms the in-memory cache backend:

curl -s http://127.0.0.1/health
{"status":"ok","timestamp":"2026-08-08T11:18:13Z","uptime":"3m35s","checks":{"cache":{"status":"ok","message":"memory"}}}

Step 5: Open the lookup web UI

Browse to http://<vm-ip>/. The SecureAnyCloud lookup page lets you query any domain, IP address, CIDR prefix or AS number, and it renders the parsed registry data. You can also link straight to a result with the query in the URL fragment, for example http://<vm-ip>/#8.8.8.8.

Step 6: Query a domain over the API

Every lookup is also a plain HTTP GET. The /api/ path is the rate-limited nginx front door to the query engine. Ask it for a domain and you get structured RDAP JSON with the registrar, the registration and expiry dates, the domain status codes and the nameservers:

curl -s http://127.0.0.1/api/google.com | jq '{ldhName,registrar,registrarIanaId,registrationDate,expirationDate,status,nameservers}'
{ "ldhName": "google.com", "registrar": "MarkMonitor Inc.", "registrarIanaId": "292", "registrationDate": "1997-09-15T04:00:00Z", "expirationDate": "2028-09-14T04:00:00Z", "status": [ "client delete prohibited", "client transfer prohibited", "client update prohibited", "server delete prohibited", "server transfer prohibited", "server update prohibited" ], "nameservers": [ "ns1.google.com", "ns2.google.com", "ns3.google.com", "ns4.google.com" ] }

This is genuine registry data, fetched live and parsed. It is not a canned response. Add ?raw=1 to a domain query to get the unparsed WHOIS text instead.

Step 7: IP and ASN lookups, and the front-door allowlist

The same endpoint auto-detects IPv4/IPv6 addresses, CIDR prefixes and AS numbers. The typed paths /api/ip/... and /api/autnum/... are available too:

curl -s http://127.0.0.1/api/8.8.8.8 | jq '{objectClassName,handle,name,startAddress,endAddress,cidr}'
curl -s http://127.0.0.1/api/AS15169 | jq '{objectClassName,handle,name}'
{ "objectClassName": "ip network", "handle": "NET-8-8-8-0-2", "name": "GOGL", "startAddress": "8.8.8.0", "endAddress": "8.8.8.255", "cidr": "8.8.8.0/24" } { "objectClassName": "autnum", "handle": "AS15169", "name": "GOOGLE" }

The nginx front door only publishes the UI, /api/ and /health. The binary also exposes operational and AI-integration endpoints (/metrics, /info, /mcp, /openapi.json) and the bulk /batch endpoint. These are not reachable from the internet. They return 404 at the front door and are available only on-box via 127.0.0.1:8043:

for p in /metrics /info /mcp /openapi.json /batch; do echo "$p -> $(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1$p)" done
/metrics -> 404 /info -> 404 /mcp -> 404 /openapi.json -> 404 /batch -> 404

The same lookup works for any domain. The web UI renders a second example the same way.

Step 8: Per-VM instance info and hardening

Every VM writes its own info file at first boot, with the URLs for this instance and a summary of the hardening in the image:

sudo cat /root/whois-api-server-info.txt

Why this is safe to expose as a public lookup: the core lookup is read-only and carries no credential, so there is nothing to compromise. The abuse surface is closed by design:

  • The app port is fenced to loopback.
  • /batch is disabled.
  • The ?refresh cache-bypass is refused while the instance is open, so nobody can force the proxy to hammer registries.
  • The upstream concurrency limit caps how many queries the proxy makes at once.
  • nginx rate-limits the /api/ path per client IP, so it cannot be used as an unbounded open relay.

In-memory caching means repeated queries are answered without touching upstream at all.

Step 9: Require an API key (optional lock-down)

To restrict the lookup itself to authenticated clients, set one or more API keys. Every endpoint except /health and /ready then requires an Authorization: Bearer <key> or X-API-Key: <key> header. Clients with a key may also use ?refresh and per-key rate limits.

Add a systemd override and restart the service:

sudo mkdir -p /etc/systemd/system/whois-api-server.service.d
sudo tee /etc/systemd/system/whois-api-server.service.d/apikey.conf >/dev/null <<'EOF' [Service] Environment=WHOIS_AUTH_KEYS=<your-token> EOF
sudo systemctl daemon-reload && sudo systemctl restart whois-api-server

You can also edit auth.keys (and enable batch) directly in /etc/whois/config.yaml. See the upstream configuration reference for per-key names and rate limits.

Server Components

  • whois-api-server 1.2.0: the WHOIS/RDAP query API (single native Go binary) at /opt/whois-api-server/whois-server, run as the whois service account.
  • nginx: the reverse proxy and front door on :80. It serves the lookup UI and proxies /api/ and /health to the loopback API, with server_tokens off and a per-IP limit_req rate limit.
  • nftables: a loopback fence (/etc/nftables.conf) that drops non-loopback inbound traffic to tcp/8043.
  • SecureAnyCloud lookup web UI: a self-contained HTML/JS page served from /var/lib/whois-api-server/viewer.

Filesystem Layout

  • /opt/whois-api-server/whois-server: the binary.
  • /opt/whois-api-server/LICENSE and /opt/whois-api-server/COMPONENTS.txt: the upstream MIT licence and the component notice.
  • /etc/whois/config.yaml: the hardened configuration (the working directory of the unit).
  • /var/lib/whois-api-server/viewer/index.html: the lookup web UI.
  • /etc/nftables.conf: the app-port loopback fence.
  • /root/whois-api-server-info.txt: the per-VM instance info written at first boot.

Managing the service

systemctl status whois-api-server --no-pager sudo journalctl -u whois-api-server -n 50 --no-pager

After you change the configuration, restart the API with sudo systemctl restart whois-api-server.

Security

The API runs as a non-root service account. The app port is fenced to loopback with nftables, and nginx is the only public surface. A strict path allowlist means the operational, MCP and batch endpoints are never exposed to the internet.

The lookup is public by design and ships no credential. Set WHOIS_AUTH_KEYS (Step 9) to require an API key on every endpoint. nginx rate-limits /api/ per client IP and the binary caps concurrent upstream queries, so the proxy cannot be abused as an unbounded open relay.

Keep the OS patched. Unattended security updates are enabled in the image.

Troubleshooting

  • A lookup times out or returns an upstream error. The VM needs outbound access to the WHOIS (43/tcp) and RDAP (443/tcp) servers. Check the NSG and any egress firewall.
  • /api/... returns 502. The API is not running. Check systemctl status whois-api-server and sudo journalctl -u whois-api-server. The unit is gated on the first-boot marker /var/lib/sac/data/whois-api-server-firstboot.done.
  • /api/... returns 401 after you set a key. The instance now requires an API key. Send Authorization: Bearer <key>, or unset WHOIS_AUTH_KEYS to reopen it.
  • /api/... returns 429. The per-IP rate limit engaged. Tune limit_req in /etc/nginx/conf.d/sac-whois-api-server-hardening.conf for your traffic.

Support

SecureAnyCloud images include 24/7 support. Contact sales@secureanycloud.com with the offer name and your VM details.

Deploy on Azure

Launch whois-api-server on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure