Admin API

View Source

The erldns Admin API provides a RESTful HTTP interface for managing and querying DNS zones at runtime. It allows you to inspect zone data, query individual records, and perform administrative operations on the DNS server.

Overview

The Admin API is built on Cowboy, a high-performance HTTP server for Erlang/OTP. It supports:

  • JSON, HTML, and plain text responses via content negotiation
  • Optional TLS encryption
  • Optional HTTP Basic Authentication
  • Custom middleware and route extensions

Default Configuration

SettingDefault Value
Host0.0.0.0 (all interfaces)
Port8083 (clear) / 8483 (TLS)
TLSDisabled
AuthenticationDisabled

Endpoints

List All Zones

Returns metadata about all zones currently loaded in the cache.

GET /

Response

{
  "erldns": {
    "zones": {
      "count": 2,
      "versions": [
        {
          "name": "example.com",
          "version": "1"
        },
        {
          "name": "example.org",
          "version": "2"
        }
      ]
    }
  }
}

Fields

FieldTypeDescription
countintegerTotal number of zones in cache
versionsarrayList of zone metadata objects
versions[].namestringZone name (domain)
versions[].versionstringZone version identifier

Reset Listener Queues

Resets all DNS listener queues. This can be useful for clearing backlogged requests.

DELETE /

Response

HTTP/1.1 204 No Content

Get Zone

Returns detailed information about a specific zone, including all its DNS records.

GET /zones/:zonename

Path Parameters

ParameterTypeDescription
zonenamestringThe zone name (e.g., example.com)

Query Parameters

ParameterTypeDefaultDescription
metaonlystringfalseSet to true to return only metadata without records

Response (Full)

{
  "erldns": {
    "zone": {
      "name": "example.com",
      "version": "1",
      "records_count": 5,
      "records": [
        {
          "name": "example.com.",
          "type": "SOA",
          "ttl": 3600,
          "content": "ns1.example.com. admin.example.com. 2024010101 3600 900 604800 86400"
        },
        {
          "name": "example.com.",
          "type": "NS",
          "ttl": 3600,
          "content": "ns1.example.com."
        },
        {
          "name": "example.com.",
          "type": "A",
          "ttl": 3600,
          "content": "192.0.2.1"
        }
      ]
    }
  }
}

Response (Metadata Only)

When ?metaonly=true:

{
  "erldns": {
    "zone": {
      "name": "example.com",
      "version": "1",
      "records_count": 5
    }
  }
}

Fields

FieldTypeDescription
namestringZone name
versionstringZone version identifier
records_countintegerTotal number of records in the zone
recordsarrayList of DNS records (omitted when metaonly=true)
records[].namestringFully qualified record name
records[].typestringDNS record type (A, AAAA, CNAME, MX, etc.)
records[].ttlintegerTime-to-live in seconds
records[].contentstringRecord data (format varies by type)

Error Response

If the zone is not found:

HTTP/1.1 404 Not Found

Delete Zone

Removes a zone from the cache.

DELETE /zones/:zonename

Path Parameters

ParameterTypeDescription
zonenamestringThe zone name to delete

Response

HTTP/1.1 204 No Content

Error Response

If the zone is not found:

HTTP/1.1 400 Bad Request
Content-Type: application/json

{"error": "zone not found"}

List Zone Records

Returns all DNS records in a zone.

GET /zones/:zonename/records

Path Parameters

ParameterTypeDescription
zonenamestringThe zone name

Response

[
  {
    "name": "example.com.",
    "type": "SOA",
    "ttl": 3600,
    "content": "ns1.example.com. admin.example.com. 2024010101 3600 900 604800 86400"
  },
  {
    "name": "example.com.",
    "type": "A",
    "ttl": 3600,
    "content": "192.0.2.1"
  },
  {
    "name": "www.example.com.",
    "type": "CNAME",
    "ttl": 300,
    "content": "example.com."
  }
]

Get Records by Name

Returns DNS records matching a specific name within a zone.

GET /zones/:zonename/records/:record_name

Path Parameters

ParameterTypeDescription
zonenamestringThe zone name
record_namestringThe record name to filter by (e.g., www or www.example.com)

Query Parameters

ParameterTypeDescription
typestringFilter by DNS record type (e.g., A, AAAA, CNAME, MX)

Examples

Get all records for www.example.com:

GET /zones/example.com/records/www.example.com

Get only A records for www.example.com:

GET /zones/example.com/records/www.example.com?type=A

Response

[
  {
    "name": "www.example.com.",
    "type": "A",
    "ttl": 300,
    "content": "192.0.2.1"
  }
]

Authentication

The Admin API supports HTTP Basic Authentication. When enabled, all endpoints require valid credentials. Authentication is disabled by default.

How Authentication Works

Authentication is implemented as a Cowboy middleware (erldns_admin_auth_middleware). When credentials are configured, the middleware is automatically injected into the request processing pipeline before any handlers are invoked. This ensures that all endpoints are protected without requiring changes to individual handlers.

The middleware:

  1. Intercepts every incoming request
  2. Parses the Authorization header for Basic Auth credentials
  3. Returns 401 Unauthorized if credentials are missing or invalid
  4. Allows the request to proceed if credentials are valid

Enabling Authentication

To enable authentication, configure the credentials option in your sys.config or erldns.config:

{erldns, [
    {admin, [
        {credentials, {<<"admin">>, <<"secret">>}}
    ]}
]}

Both username and password must be binary strings (using <<"...">> syntax).

Disabling Authentication

Authentication is disabled by default. To explicitly disable it (or to disable it after it was enabled), set credentials to false:

{erldns, [
    {admin, [
        {credentials, false}
    ]}
]}

When authentication is disabled, the auth middleware is not added to the request pipeline, and all endpoints are publicly accessible.

Making Authenticated Requests

Include the Authorization header with Base64-encoded credentials:

curl -u admin:secret http://localhost:8083/zones/example.com

Or manually construct the header:

curl -H "Authorization: Basic YWRtaW46c2VjcmV0" http://localhost:8083/zones/example.com

The Base64-encoded value is username:password encoded. For example, admin:secret encodes to YWRtaW46c2VjcmV0.

Unauthorized Response

When authentication fails or credentials are missing:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: basic realm="erldns admin"

Security Considerations

  • Use TLS in production: HTTP Basic Authentication transmits credentials encoded (not encrypted). Always enable TLS when using authentication in production to protect credentials in transit.
  • Strong credentials: Use long, random passwords. Consider using a password generator for production deployments.

Example with both TLS and authentication:

{erldns, [
    {admin, [
        {port, 8483},
        {tls, {true, [
            {certfile, "/path/to/cert.pem"},
            {keyfile, "/path/to/key.pem"}
        ]}},
        {credentials, {<<"admin">>, <<"strong-random-password">>}}
    ]}
]}

TLS Configuration

Enable HTTPS by configuring TLS options:

{erldns, [
    {admin, [
        {port, 8483},
        {tls, {true, [
            {certfile, "/path/to/cert.pem"},
            {keyfile, "/path/to/key.pem"}
        ]}}
    ]}
]}

The TLS options are passed directly to Erlang's ssl module. See the ssl documentation for all available options.

Common TLS Options

OptionDescription
certfilePath to the PEM-encoded certificate file
keyfilePath to the PEM-encoded private key file
cacertfilePath to the CA certificate file for client verification
verifyverify_peer or verify_none for client certificate verification

Configuration Reference

All configuration options are set under {erldns, [{admin, [...]}]}:

{erldns, [
    {admin, [
        {port, 8083},
        {tls, false},
        {credentials, false},
        {middleware, []},
        {routes, []}
    ]}
]}

Options

OptionTypeDefaultDescription
portinteger8083 / 8483Port to listen on (1-65535)
tlsfalse | {true, SslOpts}falseTLS configuration
credentialsfalse | {User, Pass}falseHTTP Basic Auth credentials (binaries)
middlewarelist[]Custom Cowboy middleware modules
routeslist[]Additional Cowboy routes

Extending the Admin API

Custom Middleware

Middleware modules intercept all requests before they reach the handlers. This is useful for logging, metrics, custom authentication, or request modification.

Creating Middleware

Implement the cowboy_middleware behavior:

-module(my_admin_middleware).
-behaviour(cowboy_middleware).

-export([execute/2]).

execute(Req, Env) ->
    %% Log the request
    logger:info("Admin API request: ~s ~s", [
        cowboy_req:method(Req),
        cowboy_req:path(Req)
    ]),

    %% Add a custom response header
    Req2 = cowboy_req:set_resp_header(<<"x-custom-header">>, <<"value">>, Req),

    %% Continue processing
    {ok, Req2, Env}.

Return Values

ReturnEffect
{ok, Req, Env}Continue to next middleware/handler
{stop, Req}Stop processing and return response

Registering Middleware

Add the module to the middleware configuration:

{admin, [
    {middleware, [my_admin_middleware]}
]}

Middleware executes in order, before the built-in authentication middleware.

Custom Routes

Add new HTTP endpoints without modifying the core erldns code.

Creating a Handler

Implement a Cowboy REST handler:

-module(my_custom_handler).
-behaviour(cowboy_rest).

-export([
    init/2,
    allowed_methods/2,
    content_types_provided/2,
    to_json/2
]).

init(Req, State) ->
    {cowboy_rest, Req, State}.

allowed_methods(Req, State) ->
    {[<<"GET">>], Req, State}.

content_types_provided(Req, State) ->
    {[
        {<<"application/json">>, to_json}
    ], Req, State}.

to_json(Req, State) ->
    Action = cowboy_req:binding(action, Req),
    Body = json:encode(#{action => Action, status => <<"ok">>}),
    {Body, Req, State}.

Registering Routes

Add routes to the configuration:

{admin, [
    {routes, [
        {"/custom/:action", my_custom_handler, #{}}
    ]}
]}

Custom routes are prepended to the default routes, so they take precedence if paths overlap.

Route Path Syntax

Routes use Cowboy's path matching:

PatternExample MatchBinding
/static/staticNone
/zones/:name/zones/example.comname = "example.com"
/files/[...]/files/a/b/cRest = ["a", "b", "c"]

Content Negotiation

All endpoints support content negotiation via the Accept header:

Accept HeaderResponse Format
application/jsonJSON (default)
text/htmlHTML
text/plainPlain text

Example:

curl -H "Accept: text/html" http://localhost:8083/

Error Handling

The API uses standard HTTP status codes:

CodeMeaning
200 OKRequest successful
204 No ContentDelete operation successful
400 Bad RequestInvalid request or operation failed
401 UnauthorizedAuthentication required or failed
404 Not FoundZone or record not found

Error responses include a JSON body with details:

{
  "error": "zone not found"
}

Examples

List All Zones

curl http://localhost:8083/

Get Zone with Authentication

curl -u admin:secret http://localhost:8083/zones/example.com

Get Zone Metadata Only

curl "http://localhost:8083/zones/example.com?metaonly=true"

Get All MX Records

curl "http://localhost:8083/zones/example.com/records/example.com?type=MX"

Delete a Zone

curl -X DELETE http://localhost:8083/zones/example.com

Reset Listener Queues

curl -X DELETE http://localhost:8083/

Using with jq

# Get zone count
curl -s http://localhost:8083/ | jq '.erldns.zones.count'

# List all zone names
curl -s http://localhost:8083/ | jq -r '.erldns.zones.versions[].name'

# Get all A records from a zone
curl -s http://localhost:8083/zones/example.com/records | jq '[.[] | select(.type == "A")]'

Architecture

The Admin API consists of:

  • erldns_admin - Main supervisor and startup module
  • erldns_admin_root_handler - Handles / endpoint
  • erldns_admin_zone_handler - Handles /zones/:zonename endpoint
  • erldns_admin_zone_records_handler - Handles /zones/:zonename/records[/:record_name] endpoint
  • erldns_admin_auth_middleware - HTTP Basic Authentication middleware

All handlers implement the cowboy_rest behavior and use the built-in OTP json module for encoding/decoding.