Deployment guide

Zonemaster 2026.1.2

Overview

This guide covers launching and using Zonemaster 2026.1.2 on Ubuntu 24.04 from SecureAnyCloud on Amazon EC2. It is one version of the SecureAnyCloud Hardened Pi-hole, AdGuard Home & DNS Server AMIs listing on AWS Marketplace. You pick it as the software version when you subscribe and launch. The other DNS and time servers in the listing are described on the listing's guide hub.

Zonemaster is an open source DNS delegation and DNSSEC quality checker, maintained jointly by The Swedish Internet Foundation (IIS) and AFNIC. Give it a domain and it investigates the zone from the root of the DNS tree down to the domain's own name servers. It runs the Zonemaster test suite (Address, Basic, Connectivity, Consistency, Delegation, DNSSEC, Nameserver, Syntax, System and Zone) and returns a graded report of Info, Notice, Warning, Error and Critical findings.

You drive Zonemaster from a web GUI or from its JSON-RPC API. It runs no DNS server of its own. It only sends DNS queries to the name servers of the domains you test.

What you get:

  • A login before anything answers. Zonemaster has no login of its own and will test any domain it is given. On this image, nginx requires HTTP Basic authentication for the web GUI and the JSON-RPC API. A random password is created for user admin on your instance's first boot and stored only as a bcrypt hash, so your instance is never an open DNS-probing service. Nothing but SSH answers until first boot has created the password.
  • HTTPS from the first boot. nginx serves Zonemaster on port 443 with a certificate generated on your instance's first boot (never baked into the image). Port 80 redirects to HTTPS.
  • Zonemaster 2026.1.2, pinned and refreshed. The official zonemaster/all-in-one container for release 2026.1.2 (Engine v9.0.1, Backend v12.1.1, CLI v8.0.3, LDNS v5.1.0, GUI v5.1.0) is pinned by its image digest and shipped in the AMI, so it is never pulled at boot. Its Alpine Linux packages (Apache httpd 2.4.69, expat, zlib, OpenSSL 3.5.9) and the Perl modules with published security fixes (DBI, Net::DNS, Mojolicious, URI, HTTP::Date, Storable, Socket, HTTP::Tiny, HTML::Parser, podlators, Archive::Tar) are brought up to their fixed releases. An unused module with no stable fix (Net::IDN::Encode) is removed. Zonemaster's own code is unchanged.
  • The container on loopback, with reduced privileges. It is published on 127.0.0.1:8080 only and keeps just the Linux capabilities that its supervisor and web server need. It cannot gain new privileges, and its JSON-RPC backend and test agent run as the unprivileged zonemaster account. The backend port (5000) never leaves the container.
  • Fixed host and real client addresses. The container is always handed your configured host name, and its redirects use it over HTTPS. Requests for a host name the instance does not serve are dropped, and cross-origin write requests from other web sites are refused. nginx logs the real client address.
  • Metadata service blocked. The container, Docker and nginx cannot reach the instance metadata service, and the AMI requires IMDSv2.
  • Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on for Ubuntu and for Docker's repository.

What is on the image: Zonemaster 2026.1.2 (container sac/zonemaster-all-in-one:2026.1.2, built from zonemaster/all-in-one:v5.1.0-2), Docker Engine, nginx, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent.

Default user: ubuntu. Architecture: x86_64. Recommended instance type: m5.large.

Prerequisites

Before you launch the instance, make sure you have:

  • An AWS account subscribed to the listing, an EC2 key pair, and a VPC subnet with a route to the internet. Zonemaster needs outbound DNS (UDP and TCP port 53) to test domains.
  • A security group that allows TCP 22 (SSH) from your management network. It must also allow TCP 443 (the web GUI and API) and TCP 80 (redirects to HTTPS) from the networks your administrators connect from. Never open port 8080 or 5000.
1

Launch the instance

In the AWS Marketplace listing, choose Continue to Subscribe, then choose Continue to Configuration. Pick Zonemaster 2026.1.2 on Ubuntu 24.04 as the software version, pick your Region, then choose Launch through EC2. Choose m5.large, your key pair and a security group as described above. The container uses about 300 MiB of memory, so several domain tests run comfortably side by side.

To launch with the AWS CLI instead, replace the values in angle brackets:

aws ec2 run-instances --region <region> --image-id <ami-id-from-the-listing> \
  --instance-type m5.large --key-name <your-key-pair> \
  --security-group-ids <your-security-group> --subnet-id <your-subnet> \
  --metadata-options HttpTokens=required,HttpEndpoint=enabled \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=zonemaster}]'
2

Connect to your instance

Connect over SSH. For Zonemaster 2026.1.2 on Ubuntu 24.04, the SSH user is ubuntu:

ssh -i <your-key.pem> ubuntu@<public-ip>
3

Check the services

First boot takes under a minute. It creates the certificate and the password, starts the Zonemaster container against a fresh results database, and only then lets nginx start. Afterwards, every unit is active and the container is up, published on loopback only. Check with:

systemctl is-active docker zonemaster nginx zonemaster-firstboot
sudo docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'
sudo docker image ls --format '{{.Repository}}:{{.Tag}}'
active active active active NAMES STATUS PORTS zonemaster Up 5 minutes 127.0.0.1:8080->80/tcp sac/zonemaster-all-in-one:2026.1.2
4

Check what the network can reach

Run the port check to see which ports the instance exposes:

sudo /usr/local/sbin/zonemaster-port-check.sh
off-box TCP exactly [22 80 443]; Zonemaster on loopback 8080; backend :5000 not on the host ZONEMASTER_PORTS_OK

This is the complete network surface of a new instance: SSH, the HTTPS front door and the HTTP redirect.

5

Read your password

First boot writes this instance's web address, the user name and its password to a file that only root can read:

sudo cat /root/zonemaster-credentials.txt
# Zonemaster 2026.1.2 on Ubuntu 24.04 (sac)
# Generated on this instance's first boot. These credentials are unique to THIS instance. Keep this file secret. # Zonemaster has no login of its own: nginx requires this HTTP Basic credential for the web GUI and the JSON-RPC API. ZONEMASTER_URL=https://<public-dns-name>/ ZONEMASTER_API=https://<public-dns-name>/api/ ZONEMASTER_USER=admin ZONEMASTER_PASSWORD=<ZONEMASTER_PASSWORD> ...

None of these values exists in the image. Your instance creates the password and the certificate on its first boot. The host name is your instance's public DNS name, or its IP address when it has none. Step 10 shows how to use your own.

Without the password, the GUI and the API are both refused. Port 80 only redirects to HTTPS on your configured host name. To confirm, run:

curl -sk -o /dev/null -w 'no password: %{http_code}\n' https://127.0.0.1/en/
curl -sk -o /dev/null -w 'API without password: %{http_code}\n' -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"version_info"}' https://127.0.0.1/api/
curl -s -o /dev/null -w 'port 80: %{http_code} -> %{redirect_url}\n' http://127.0.0.1/en/
no password: 401 API without password: 401 port 80: 301 -> https://<public-dns-name>/en/
6

Sign in to the web GUI

Open https://<public-dns-name>/ in your browser. The certificate is self-signed until you install your own (Step 10), so your browser asks you to accept it once. It then prompts for a user name and password. Sign in as admin with the password from Step 5. You land on the Zonemaster domain checkup page.

7

Test a domain and read the graded report

Type a domain, for example zonemaster.net, and select Run test. Zonemaster walks the delegation from the root to the domain's own name servers. Usually within a minute, it shows the graded report.

The Filter severity levels row counts the findings as Info, Notice, Warning, Error and Critical, so you can see the health of the zone at a glance. Show options lets you test IPv4 or IPv6 only. It also lets you test a domain that is not yet delegated, by giving its name servers and DS records yourself.

Below the summary, the findings are grouped by test module. Each module carries badges with its per-severity counts and expands to show every test case with its message. Expand all modules opens them all, and the search box finds a specific message. History lists earlier tests of the same domain, and Export downloads the report.

Internationalised domain names work too. Enter them in their native script (for example räksmörgås.se) and Zonemaster tests the punycode form.

8

Automate with the JSON-RPC API

The backend that powers the GUI is a JSON-RPC API at https://<public-dns-name>/api/, behind the same login. The commands on this page read the password from your credentials file. They hand it to curl as a configuration file on its standard input (-K -), so it never appears on a command line or in your shell history. Ask for the component versions:

zm_auth() {
sudo sed -n 's/^ZONEMASTER_PASSWORD=\(.*\)/user = "admin:\1"/p' /root/zonemaster-credentials.txt; } zm_auth | curl -sk -K - -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"version_info"}' https://127.0.0.1/api/ | jq -c .result
{"zonemaster_ldns":"5.1.0","zonemaster_backend":"12.1.1","zonemaster_engine":"v9.0.1"}

Start a test and wait for test_progress to reach 100. Then fetch the graded results and count them by severity:

zm_auth() {
sudo sed -n 's/^ZONEMASTER_PASSWORD=\(.*\)/user = "admin:\1"/p' /root/zonemaster-credentials.txt; } zm_rpc() { zm_auth | curl -sk -K - -H 'Content-Type: application/json' -d "$1" https://127.0.0.1/api/; }
ID=$(zm_rpc '{"jsonrpc":"2.0","id":1,"method":"start_domain_test","params":{"domain":"iis.se","ipv4":true,"ipv6":false}}' | jq -r .result)
echo "test id: $ID" until [ "$(zm_rpc "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"test_progress\",\"params\":{\"test_id\":\"$ID\"}}" | jq -r .result)" = 100 ]; do sleep 5; done zm_rpc "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"get_test_results\",\"params\":{\"id\":\"$ID\",\"language\":\"en\"}}" \
  | jq -r '.result.results | group_by(.level) | map("\(.[0].level): \(length)") | .[]'
test id: fe364f3c94d0da29 INFO: 78 NOTICE: 1

Each result carries its module, testcase, level and a human-readable message. The same test is visible in the GUI at https://<public-dns-name>/en/result/<test id>.

From your own workstation, call the API as curl -k -u admin https://<public-dns-name>/api/ ..., which prompts for the password. The full method list is in the Zonemaster Backend API documentation at doc.zonemaster.net.

9

Check the login end to end

The image carries a self-test that proves the whole front door. It checks that no password and the usual weak passwords are refused for the GUI and the API, that your instance's password works, that a real domain test returns a graded report, and that the container's own ports are refused on the network address. Run it with:

sudo /usr/local/sbin/zonemaster-roundtrip.sh
no credential and weak credentials refused (401 from the nginx Basic gate) for the GUI and the API per-instance credential -> GUI 200 and JSON-RPC version_info (Engine v9.0.1, Backend 12.1.1, LDNS 5.1.0) zonemaster.net: graded report with 84 results across 10 test modules; :5000/:8080 refused on <private-ip> ZONEMASTER_LOGIN_OK
10

Use your own DNS name, certificate and password

Create a DNS record for your instance (for example zonemaster.example.com), then serve Zonemaster under that name. The helper creates a new certificate for the name and pins nginx, the cross-origin guard and the host the container sees to it:

sudo zonemaster-set-hostname zonemaster.example.com

The helper remembers the name in /etc/sac/zonemaster-hostname. To use a certificate from your own certificate authority, replace /etc/ssl/zonemaster/zonemaster.crt and /etc/ssl/zonemaster/zonemaster.key (keep the key 0600 root) and run sudo systemctl reload nginx.

sudo zonemaster-set-password creates a new random password and updates /root/zonemaster-credentials.txt. sudo zonemaster-set-password --stdin reads a password you choose (12 or more characters) from standard input. nginx picks up the new password on the next request.

sudo zonemaster-set-password
11

Metadata access is blocked

Zonemaster sends queries to whatever name servers a test points it at. The image drops any container traffic to the instance metadata service in Docker's DOCKER-USER firewall chain. systemd also denies Docker, the Zonemaster unit and nginx access to it. To check, run:

sudo iptables -S DOCKER-USER
sudo docker exec zonemaster perl -MHTTP::Tiny -e 'print "metadata from inside the container: ", HTTP::Tiny->new(timeout => 5)->get("http://169.254.169.254/latest/meta-data/")->{status}, "\n"'
-N DOCKER-USER -A DOCKER-USER -d 168.63.129.16/32 -j DROP -A DOCKER-USER -d 169.254.169.254/32 -j DROP metadata from inside the container: 599

599 means the connection never completed. The instance itself still uses IMDSv2 normally, and first boot reads its host name from it.

12

What SecureAnyCloud changed in the container, and updates

The image's build record lists the upstream container digest, the image that runs, and every package and module brought up to its security release. Read the first lines with:

sed -n '1,5p' /usr/share/doc/sac/zonemaster/BUILD-PROVENANCE.txt
Zonemaster 2026.1.2 on Ubuntu 24.04 - sac build provenance ===================================================================== Upstream product release : Zonemaster v2026.1.2 (Engine v9.0.1, CLI v8.0.3, Backend v12.1.1, LDNS v5.1.0, GUI v5.1.0) Upstream container : zonemaster/all-in-one@sha256:ba35602d29298c1bbd9c121ea8e6f3d1fe9f73d11d1440bae57df86e310e0ada (zonemaster/all-in-one:v5.1.0-2, linux/amd64, 2026-10-01) Runtime image : sac/zonemaster-all-in-one:2026.1.2 id sha256:<image-id> (one layer)
  • Ubuntu and Docker receive security updates automatically through unattended-upgrades.
  • Zonemaster is the pinned 2026.1.2 container. SecureAnyCloud publishes a new version of this listing when a new Zonemaster release, or a security update to its container, is needed. Test results live in the Docker volume zonemaster_db. Launch the new version when you want to move, and keep the old instance until you no longer need its history.

Security recommendations

  • Open ports 80 and 443 only to the networks your administrators use, and port 22 only to your management network. Anyone who signs in can make the instance send DNS queries to any domain or name server.
  • Use your own DNS name and a certificate your browsers trust (Step 10). Change the password if it has been shared.
  • The HTTP Basic password is a single shared credential for the GUI and the API. Give API automation its own instance, or rotate the password when people leave.

Support and licensing

SecureAnyCloud provides 24/7 support for this image at sales@secureanycloud.com.

Zonemaster is licensed under the BSD 2-Clause licence (documentation CC BY 4.0). The verbatim licence and a notice are in /usr/share/doc/sac/third-party-licences/, and the build record is in /usr/share/doc/sac/zonemaster/.

Zonemaster is a project of The Swedish Internet Foundation and AFNIC. SecureAnyCloud provides this image, which is not affiliated with or endorsed by the Zonemaster project, The Swedish Internet Foundation or AFNIC. The Zonemaster name is used only to identify the software.

Deploy on AWS

Launch Zonemaster 2026.1.2 on Ubuntu 24.04 on AWS with 24/7 support from SecureAnyCloud.

View on Marketplace

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on AWS