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
- You list the hosts to watch, such as
wiki.corp.exampleor10.0.4.12:8443. - The agent connects to each one, reads the certificate chain it presents and notes the TLS version.
- It sends that to CertHalo over HTTPS, the only connection it makes outside your network.
- 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 withcertutil -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 -showcertsshows 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.