DEV Community

Germey
Germey

Posted on

Check DNS and TLS before sending an API key to a new hostname

Before a customer's first API request, verify the hostname separately from the service operation. A branded portal's website domain and API domain can have different jobs. A working homepage establishes only part of the path.

This guide uses a small Python probe to check DNS resolution and a verified TLS connection before sending any API credential. It sends no HTTP request and has no input for an API key.

Disclosure: this is a practical guide from the Ace Data Cloud team. The setup notes reference our current public documentation; the probe uses Python's standard library and can be applied to an HTTPS hostname you are authorized to check.

Start with the intended API hostname

Keep these two addresses separate in your acceptance record:

Address Intended role
portal.example.com The website where customers read documentation and manage their account
api.example.com The hostname their program uses for documented API requests

These are reserved example names. Replace them with the actual addresses from your configuration.

For Ace Data Cloud, the page-domain and API-domain controls each provide their own CNAME target. Copy the target from the correct control in the current console. Do not copy an old screenshot or replace an existing DNS record without checking what currently uses it. DNS resolution and HTTPS activation are separate checks.

The current domain setup guide covers those controls. This article focuses on the next client-side check.

Probe DNS and TLS without a key

Save the following as api_hostname_preflight.py. It uses Python 3 and the runtime's normal trust store. The argument is a bare hostname: no scheme, port, path, query string or credentials.

import argparse
import json
import socket
import ssl
from datetime import datetime, timezone


def main():
    parser = argparse.ArgumentParser(description="Resolve a hostname and verify TLS; send no HTTP request")
    parser.add_argument("hostname", help="Bare hostname, without scheme, port, path or credentials")
    args = parser.parse_args()
    host = args.hostname.rstrip(".")
    if not host or any(c in host for c in "/:@?#") or any(c.isspace() for c in host):
        parser.error("Pass a hostname only, not a URL, port or credential")
    try:
        host = host.encode("idna").decode("ascii")
    except UnicodeError:
        parser.error("Hostname could not be converted to an ASCII DNS name")

    report = {"hostname": host, "port": 443,
              "started_at": datetime.now(timezone.utc).isoformat()}
    stage = "dns"
    try:
        answers = socket.getaddrinfo(host, 443, type=socket.SOCK_STREAM)
        report["resolved_addresses"] = sorted({answer[4][0] for answer in answers})
        stage = "connect"
        context = ssl.create_default_context()
        with socket.create_connection((host, 443), timeout=5) as connection:
            stage = "tls"
            with context.wrap_socket(connection, server_hostname=host) as tls:
                certificate = tls.getpeercert()
                report.update(status="tls_verified", connected_peer=tls.getpeername()[0],
                              tls_version=tls.version(),
                              certificate_not_after=certificate.get("notAfter"))
    except ssl.SSLCertVerificationError as error:
        report.update(status="certificate_rejected", stage=stage,
                      verification_code=error.verify_code)
    except (OSError, ValueError) as error:
        report.update(status="probe_failed", stage=stage, error_type=type(error).__name__)
    report["checked_at"] = datetime.now(timezone.utc).isoformat()
    print(json.dumps(report, indent=2))
    return 0 if report["status"] == "tls_verified" else 1


if __name__ == "__main__":
    raise SystemExit(main())
Enter fullscreen mode Exit fullscreen mode

Run it against the API hostname you intend to configure:

python3 api_hostname_preflight.py api.example.com
Enter fullscreen mode Exit fullscreen mode

The code opens port 443, verifies the certificate using the configured trust store, and closes the connection. It does not send a request path, an Authorization header, a cookie or a generation request.

ssl.create_default_context() enables certificate verification. Passing server_hostname=host lets TLS use the hostname for SNI and hostname verification. Do not replace this with an unverified context to make a failing check appear successful.

The five-second timeout applies to connection and TLS socket operations. Operating-system DNS resolution has its own behavior, and trying multiple addresses can make the total run take longer than five seconds. This is a small diagnostic probe, not a full monitoring agent.

Read the result at the right level

The result is JSON, with UTC timestamps and a clear stage:

Result What it tells you What to check next
tls_verified This client resolved the name and accepted a TLS connection for it using this runtime's trust configuration The intended application route and later authenticated API behavior
certificate_rejected Certificate verification failed The hostname, certificate validity and chain, local clock and trust configuration
probe_failed, stage dns The system resolver did not return usable address information The configured name, DNS records and the client's resolver
probe_failed, stage connect Address resolution completed, but connection setup or its prerequisites failed Reachability, port 443, local network policy and client configuration
probe_failed, stage tls A connection was made, but the TLS operation failed or timed out TLS configuration and the network path; this is not an API-key response
Argument error, exit code 2 The input was not a supported hostname Remove the scheme, port, path or credentials before probing

On success, resolved_addresses records the local resolver's answers, while connected_peer records the connection actually selected. The connection helper may resolve the name again, so those observations can differ. A successful connection checks one selected address; it does not certify every A or AAAA record.

This is also not an authoritative DNS lookup or a check of the entire CNAME chain. Resolvers, proxies and trust stores can change what a particular machine observes. Some networks synthesize addresses even for names that do not exist, which can move an eventual failure from the DNS stage to the connection or TLS stage. Preserve the observed stage instead of assigning an expected one. Compare the configured CNAME with the current setup instructions separately.

Keep routing and authorization as separate tests

A TLS pass establishes neither the right application route nor access to a service. A different application can present a valid certificate for the hostname. The probe intentionally makes no HTTP request, so it cannot identify that application.

After the API-domain status is ready, inspect the documented Base URL and request path. In Ace Data Cloud's Usage and Testing area, View Code helps you inspect the selected API example before running it. An example containing a real key should stay private. Manually maintained documentation needs its own address review; do not assume an old hard-coded host was rewritten automatically.

A later HTTP response is a new piece of evidence. For example, a route returning 404 or an authenticated request returning 401 should be investigated at that layer. Neither response proves that DNS is broken. Preserve the service's actual status and sanitized error rather than relabelling it.

A branded API hostname also does not create credential isolation. The credential-permission guide covers allowlists and allowed-versus-rejected operation tests separately.

Hand off a small, useful record

Keep the probe output with the intended hostname, the network it was run from, and the expected API configuration. Before sharing it, check whether hostnames or network addresses disclose information that belongs in a private support channel.

A concise handoff can look like this:

Intended API hostname:
Source of expected host / domain configuration:
Client and network used for the check:
Started and checked at (UTC):
DNS answers and connected peer, if appropriate to share:
TLS result and certificate expiry, or exact failed stage:
HTTP route check: not performed / observed result:
Authenticated operation: not performed / observed result:
Remaining uncertainty:
Enter fullscreen mode Exit fullscreen mode

Do not attach a full environment dump or a credential-bearing command. Use the support-report guide to prepare any additional logs or screenshots.

Once these transport checks and the intended route are understood, select one documented, small API operation with an authorized test customer's own credential. Confirm its current cost, inspect the actual response and usage record, and preserve any original asynchronous task ID before considering a retry. That final operation is a separate acceptance step; the DNS/TLS probe has not performed it for you.

Top comments (0)