CertHaloSSL expiry monitoring · by IncubXperts

Monitor internal certificates with a CertHalo agent

Step by step, set up a CertHalo agent inside your network to monitor intranet, API, database and appliance certificates, on Linux, Windows or Docker.

CertHalo teamUpdated 6 min read

Applies toLinuxWindowsmacOSDockerKubernetes

A CertHalo agent runs inside your network, reads the certificate each internal host presents, and reports it to CertHalo, which grades it and alerts you like any other domain. This guide takes about fifteen minutes. If you're deciding whether you need it, read why internal certificates need monitoring first.

You need a CertHalo workspace on the Business plan, and the Owner or Admin role to create keys.

How it works

  1. You list the hosts to watch, such as wiki.corp.example or 10.0.4.12:8443.
  2. The agent connects to each one, reads the certificate chain it presents and notes the TLS version.
  3. It sends that to CertHalo over HTTPS, the only connection it makes outside your network.
  4. CertHalo checks expiry, trust, hostname and key strength, and alerts you by email, Slack, Microsoft Teams or webhook.

The agent never sends private keys or anything from your servers besides their certificates, and its key can only send reports.

Step 1: Create an Agent key

In CertHalo, open Developers from the dashboard menu. Under API keys, name the key after where the agent will run (for example dc1-agent), choose Agent (sends reports only) and select Create key.

The key starts with chk_ and is shown once. Store it in your password manager or secrets store straight away.

Step 2: Upload your internal root CA (if you have one)

If your internal servers use certificates from your own certificate authority, CertHalo needs that CA's root certificate to know they're trusted. Without it, they're reported as untrusted.

On the Developers page, under Internal certificates, choose Add an internal root CA and paste the root certificate in PEM format (the block that starts -----BEGIN CERTIFICATE-----). Only the certificate: never paste the CA's private key, and CertHalo refuses it if you do.

Where to find it:

  • Active Directory Certificate Services: on the CA server, certutil -ca.cert root.cer, then convert it with certutil -encode root.cer root.pem.
  • HashiCorp Vault: vault read -field=certificate pki/cert/ca.
  • step-ca: step ca root root.pem.
  • Any server using it: openssl s_client -connect wiki.corp.example:443 -showcerts shows the chain; the last certificate is usually the issuing or root CA.

If your servers don't send their issuing (intermediate) CA, upload that too.

Step 3: Choose an agent

All agents are open source, behave the same and take the same settings. Get them from GitHub.

Agent Best for
Go binary Linux, Windows or macOS servers: one file, nothing to install (recommended)
Docker image Container hosts, and Kubernetes (guide)
PowerShell script Windows servers, with Task Scheduler
Node.js or Python script Machines that already have them

Run the agent somewhere that can reach every host on your list. If your network is split into segments that can't reach each other, run one agent in each, with different names.

Step 4: List your hosts

Create targets.txt with one host per line:

# Staff tools
wiki.corp.example
hr.corp.example
# Directory and brokers (TLS on any port)
dc1.corp.example:636
kafka1.corp.example:9093
# Appliances by address
10.0.4.12:8443

Use host, host:port or [ipv6]:port; port 443 is assumed when there isn't one. Any service that speaks TLS from the first byte works: HTTPS, LDAPS (636), Redis, Kafka and RabbitMQ (5671) with TLS, appliance management pages. Services that upgrade to TLS within their own protocol, such as PostgreSQL, MySQL, SMTP and LDAP on 389, aren't supported yet. Write each host the way people and programs reach it, so CertHalo can check the certificate actually covers that name.

Step 5: See what would be sent

Run a dry run first. Nothing is sent, and no key is needed:

./certhalo-agent-linux-amd64 --targets targets.txt --dry-run

For each host you'll see either a chain (the certificates it presented) or an error (why the agent couldn't connect: dns, timeout, refused, reset or not_tls). Fix any errors that aren't expected, for example a typo in a name or a firewall rule blocking the agent, before going further.

On macOS, a binary downloaded in a browser needs xattr -d com.apple.quarantine first; on Windows, SmartScreen may ask you to confirm. The agents README has the details.

Step 6: Send the first report

export CERTHALO_API_KEY=chk_...   # the Agent key from step 1
./certhalo-agent-linux-amd64 --targets targets.txt --name dc1-agent

It prints how many hosts were accepted. Any that weren't are listed with the reason. Open the CertHalo dashboard: your hosts appear with an Internal badge, the agent's name and when it last reported.

Step 7: Run it on a schedule

The agent reports every 6 hours by default and tells CertHalo so. If nothing arrives for twice that long, CertHalo sends a "no report" alert, so a stopped agent never looks like "all fine". Schedule it to match.

Linux (systemd). Copy the binary to /usr/local/bin/certhalo-agent, put the key in /etc/certhalo-agent.env (CERTHALO_API_KEY=chk_..., readable by root only), and use the example service and timer:

systemctl enable --now certhalo-agent.timer

Windows. Use the PowerShell agent with Task Scheduler; the example registers a task that runs every 6 hours.

Docker. The image keeps running and reports on its own schedule:

docker run -d --restart unless-stopped --name certhalo-agent \
  -e CERTHALO_API_KEY=chk_... -e CERTHALO_AGENT_NAME=dc1-agent \
  -v /etc/certhalo-agent/targets.txt:/config/targets.txt:ro \
  ghcr.io/incubxperts/certhalo-agent:1

Cron. Any scheduler works: 0 */6 * * * certhalo-agent --targets /etc/certhalo-agent/targets.txt with the key in the environment.

Behind a proxy? The agents use the standard HTTPS_PROXY setting for their connection to CertHalo.

What you'll be alerted about

Internal certificates get the same alerts as public ones, following each person's alert preferences:

  • Expiry reminders before a certificate expires (60, 30, 14, 7, 3 and 1 day by default, or your own days).
  • Changes: renewed, a new issuer, revoked, untrusted, or no longer covering its name.
  • Can't reach host: the agent couldn't connect in two reports in a row.
  • No report: the agent itself has stopped. Check that it's running and can reach certhalo.com.

Revocation isn't checked for internal certificates: your CA's revocation servers usually aren't reachable from the internet.

Troubleshooting

What you see What to do
A certificate from your own CA is untrusted Upload the root CA (step 2), and the issuing CA if the server doesn't send it
The certificate doesn't cover the host Use the name the certificate was issued for in targets.txt, or reissue the certificate with the name people use. Appliances often still have a factory certificate for the vendor's hostname
dns errors The agent's machine can't resolve the name: check its DNS settings or use the address
timeout or refused A firewall blocks the agent, or nothing listens on that port
not_tls The port doesn't speak TLS from the start: either it's plain, or it upgrades within its own protocol (PostgreSQL, MySQL, SMTP), which agents don't support yet
401 from CertHalo The key is wrong or was revoked
402 The workspace isn't on the Business plan
403 The key is read-only or read and write: create an Agent key
A host is rejected as already monitored as a public domain It's on the dashboard as a public domain: remove it there first
"observedAt must be within the last 24 hours" Fix the agent machine's clock

Still stuck? Run with --dry-run and send us the output, with the certificates removed if you prefer, at support@certhalo.com.

Check your certificate now

See when it expires, who issued it, whether the chain is complete and if it's been revoked. Free, no sign-up.

Want a reminder before it expires? Monitor 3 certificates free.

More guides