Skip to content

Certificate Deployment Monitoring

Monitoring Module Cert

Certificate Deployment Monitoring scans your TLS endpoints directly and reports on the certificate currently being served. Unlike CT-Log monitoring — which watches for certificates being issued — this module verifies what is actually deployed on your servers: expiry, protocol versions, cipher suites, and chain validity.


How it works

  1. You create a certificate monitor for a hostname (Common Name).
  2. You attach one or more IP targets — the server addresses to scan for that hostname.
  3. NOB.center connects to each target over TLS on the configured port, performs a full handshake, and records the certificate chain, protocols, and ciphers accepted by the server.
  4. Your alert rules evaluate each scan result and dispatch notifications if conditions are met.
  5. A scan history is stored for every target so you can track how the certificate evolves over time.

Certificate monitors

A monitor represents the hostname (CN) you expect to find on the certificate. One monitor can have multiple IP targets — for example, all servers behind a load balancer, or both IPv4 and IPv6 addresses.

Creating a monitor

Navigate to Certificate Deployment and click Add Monitor. Fill in:

Field Description
Common Name (CN) The hostname the certificate should be issued for (e.g. app.example.com)
Port TCP port to connect to (default 443)
Target DNS Optional DNS name to resolve for the target (overrides IP-based scanning for SNI)
Notes Free-text label
Root CA Optional PEM-encoded root certificate to use for chain validation

Custom root CA for self-signed certificates

By default NOB.center validates the certificate chain against the system trust store. If your server presents a self-signed certificate or a certificate signed by a private CA (common in internal infrastructure, staging environments, or IoT devices), chain validation would fail and every scan would report an error.

To monitor these endpoints, paste the PEM-encoded root CA certificate into the Root CA field when creating or editing the monitor. NOB.center will use it as the trust anchor for chain validation, allowing the scan to complete successfully and report on expiry, protocols, and ciphers as normal.

Note

Providing a custom root CA does not suppress the self_signed detection in alert rules — certificate.subject == certificate.issuer still evaluates correctly. It only tells the scanner to trust that root when building the chain.

After creating the monitor, add one or more IP targets.

cert-add-monitor-modal.png

The Add Monitor modal

IP targets

Each IP target is an IP address that will be scanned for the monitor's CN. The scanner connects to <ip>:<port>, sends the CN as the TLS SNI extension, and inspects the certificate returned.

You can add multiple targets to cover: - All nodes in a cluster - IPv4 and IPv6 addresses - Primary and backup servers

cert-monitor-detail.png

Monitor detail page showing IP targets and latest scan results


Scan results

Each scan records:

Field Description
IP address The target that was scanned
Certificate subject The Common Name of the leaf certificate
Issuer The CA that issued the certificate
Valid from / until Certificate validity period
Days remaining Days until the certificate expires
TLS protocols accepted All TLS versions the server accepted
Cipher suites Full list of TLS cipher suites offered
Named groups Elliptic curve / DH groups advertised
Chain length Number of certificates in the chain
Connection method direct_tls (port 443/8443) or starttls (mail servers)
Scan status Success or error

cert-scan-history.png

Scan history for a monitor, showing one result per target per check cycle

Security framework analysis

The scan detail view shows a Framework Analysis panel. NOB.center evaluates the observed TLS configuration against several industry standards and flags compliance gaps:

Framework Checks
FIPS 140-3 Approved cipher suites and key exchange algorithms
NIST SP 800-52 TLS version requirements and cipher recommendations
PCI-DSS v4 Prohibition of TLS 1.0 and weak ciphers
Mozilla Intermediate Mozilla's recommended compatible TLS profile
Mozilla Modern Mozilla's stricter TLS 1.3-only profile

cert-framework-analysis.png

Security framework analysis panel in the scan detail view


Alert rules

Navigate to Certificate Deployment → Alert Rules to create and manage rules for this module.

CEL context object

Each scan result exposes the following fields to your CEL conditions:

{
  "monitor_cn": "app.example.com",
  "dns_name": "app.example.com",
  "ip_address": "93.184.216.34",
  "connection_method": "direct_tls",
  "protocol": ["TLSv1.2", "TLSv1.3"],
  "ciphers": [
    "TLS_AES_256_GCM_SHA384",
    "TLS_CHACHA20_POLY1305_SHA256",
    "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"
  ],
  "named_groups": ["X25519", "P-256"],
  "certificate": {
    "sha256": "a1b2c3d4e5f6...",
    "subject": "CN=app.example.com",
    "issuer": "CN=R10, O=Let's Encrypt, C=US",
    "dns_names": ["app.example.com", "www.example.com"],
    "not_before": "2026-01-01T00:00:00Z",
    "not_after": "2026-04-01T00:00:00Z",
    "serial": "12345678901234567890",
    "signature_algorithm": "SHA256-RSA",
    "public_key_algorithm": "RSA",
    "aia": "http://r10.i.lencr.org/",
    "ocsp": "http://r10.o.lencr.org/"
  },
  "validity_days": 75,
  "intermediates": [
    {
      "subject": "CN=R10, O=Let's Encrypt, C=US",
      "issuer": "CN=ISRG Root X1, O=Internet Security Research Group, C=US",
      "not_before": "2023-03-17T16:00:00Z",
      "not_after": "2027-03-13T23:59:59Z",
      "signature_sha256": "b2c3d4..."
    }
  ],
  "validation_path": [
    { "certificate_subject": "CN=app.example.com", "certificate_issuer": "CN=R10, O=Let's Encrypt, C=US" },
    { "certificate_subject": "CN=R10, O=Let's Encrypt, C=US", "certificate_issuer": "CN=ISRG Root X1, ..." }
  ],
  "has_error": false,
  "error": ""
}

Top-level fields

Field Type Description
monitor_cn string Common Name configured for the monitor
dns_name string DNS hostname used for SNI (may be empty for IP-only monitors)
ip_address string IP address that was scanned
connection_method string direct_tls or starttls
protocol list TLS versions accepted by the server
ciphers list TLS cipher suites offered (IANA names)
named_groups list Elliptic curve / DH groups advertised
certificate object Leaf certificate details
validity_days int Days until the certificate expires; negative if already expired
intermediates list Intermediate certificates in the chain
validation_path list Certificate chain from leaf to trust anchor
has_error bool true if the scan failed entirely
error string Error message; empty on success

certificate fields

Field Type Description
sha256 string SHA-256 fingerprint of the leaf certificate
subject string Subject distinguished name
issuer string Issuer distinguished name
dns_names list Subject Alternative Names (SANs)
not_before string Validity start (RFC 3339)
not_after string Expiry date (RFC 3339)
serial string Serial number
signature_algorithm string e.g. SHA256-RSA, ECDSA-SHA384
public_key_algorithm string RSA or ECDSA
aia string CA Issuers URL from AIA extension
ocsp string OCSP responder URL

intermediates fields

Field Type Description
subject string Subject DN of the intermediate
issuer string Issuer DN of the intermediate
not_before string RFC 3339 validity start
not_after string RFC 3339 validity end
signature_sha256 string SHA-256 fingerprint

Example rules

Certificate expiring within 30 days:

!has_error && validity_days < 30

Certificate already expired:

!has_error && validity_days < 0

Scan failed (connection refused, TLS error):

has_error

TLS 1.0 or 1.1 accepted (deprecated):

!has_error && protocol.exists(p, p == "TLSv1" || p == "TLSv1.1")

TLS 1.3 not supported:

!has_error && !protocol.exists(p, p == "TLSv1.3")

Weak cipher suite offered (RC4, DES, 3DES, NULL):

!has_error && ciphers.exists(c, c.contains("RC4") || c.contains("_DES_") || c.contains("3DES") || c.contains("NULL"))

No forward secrecy:

!has_error && !ciphers.exists(c, c.contains("DHE") || c.contains("ECDHE"))

Self-signed certificate:

!has_error && certificate.subject == certificate.issuer

Wildcard certificate:

!has_error && certificate.dns_names.exists(d, d.startsWith("*."))

SHA-1 signature (deprecated):

!has_error && certificate.signature_algorithm.contains("SHA1")

Missing OCSP responder:

!has_error && certificate.ocsp == ""

Incomplete chain (no intermediates):

!has_error && intermediates.size() == 0

CN mismatch (certificate not valid for the monitored hostname):

!has_error && !certificate.dns_names.exists(d, d == monitor_cn)


API reference

All Certificate Deployment endpoints require authentication. See Authentication.

GET /api/cert-watcher/monitors

List your organization's certificate monitors.

Permission: view_certificates

Query parameters: page (int, default 1), page_size (int, default 20)

Response:

{
  "monitors": [
    {
      "id": 1,
      "cn": "app.example.com",
      "enabled": true,
      "port": 443,
      "target_dns": null,
      "notes": "Production API",
      "created_at": "2026-01-01T00:00:00Z",
      "latest_scan": {
        "scanned_at": "2026-01-15T10:00:00Z",
        "validity_days": 75,
        "has_error": false
      }
    }
  ],
  "total": 1,
  "page": 1,
  "page_size": 20
}

GET /api/cert-watcher/monitors/{monitor_id}

Get details for a single monitor, including its IP targets.

Permission: view_certificates

POST /api/cert-watcher/monitors

Create a new certificate monitor.

Permission: create_certificates

Body:

Field Type Required Description
cn string Yes Common Name (hostname)
port int No Default 443
target_dns string No Override DNS name for SNI
enabled bool No Default true
notes string No
root_ca string No PEM-encoded root certificate
ip_addresses list No Initial list of IP targets to add

Response: {"id": 1, "message": "Monitor created", "quota_remaining": 9}

PUT /api/cert-watcher/monitors/{monitor_id}

Replace all settings of an existing monitor.

Permission: update_certificates

Response: {"message": "Monitor updated"}

PATCH /api/cert-watcher/monitors/{monitor_id}

Toggle enabled/disabled state.

Permission: update_certificates

Body: {"enabled": false}

Response: {"message": "Monitor updated"}

DELETE /api/cert-watcher/monitors/{monitor_id}

Delete a certificate monitor and all its targets.

Permission: delete_certificates

Response: {"message": "Monitor deleted"}

GET /api/cert-watcher/history/{monitor_id}

Get scan history for a monitor, with framework-based strength analysis per result.

Permission: view_history

Query parameters: page (int, default 1), page_size (int, default 20)

Response: List of scan result objects, each including protocol/cipher data and framework compliance flags.

GET /api/cert-watcher/security-standards

Return the full definitions of the supported security standards (FIPS, NIST, PCI-DSS, Mozilla).

Permission: module access (any role)

GET /api/cert-watcher/ct-log-list

Return the current list of known CT logs (sourced from Google's public log list, cached 24 hours). Useful for cross-referencing SCTs.

Permission: module access (any role)

GET /api/cert-watcher/introspection

Returns the CEL variable schema for this module, used by the rule editor's Available fields dropdown.

Permission: module access (any role)