From bf4ea57d3c96ef5820934582b0b859afbab8c512 Mon Sep 17 00:00:00 2001 From: Daniel Paulus Date: Wed, 8 Jul 2026 15:22:16 +0200 Subject: [PATCH 1/3] docs: add SSL monitor documentation Adds overview, configuration, and CLI construct reference pages for SSL monitors. Config sourced from the checkly-cli SslMonitor/SslRequest constructs and the go-runner ssl package. --- constructs/ssl-monitor.mdx | 498 ++++++++++++++++++ .../ssl-monitors/configuration.mdx | 113 ++++ .../ssl-monitors/overview.mdx | 94 ++++ docs.json | 8 + 4 files changed, 713 insertions(+) create mode 100644 constructs/ssl-monitor.mdx create mode 100644 detect/uptime-monitoring/ssl-monitors/configuration.mdx create mode 100644 detect/uptime-monitoring/ssl-monitors/overview.mdx diff --git a/constructs/ssl-monitor.mdx b/constructs/ssl-monitor.mdx new file mode 100644 index 00000000..2d1b2c8c --- /dev/null +++ b/constructs/ssl-monitor.mdx @@ -0,0 +1,498 @@ +--- +title: 'SslMonitor Construct' +description: 'Learn how to configure SSL monitors with the Checkly CLI.' +sidebarTitle: 'SSL Monitor' +--- + +import GeneralMonitorOptionsTable from '/snippets/general-monitor-options-table.mdx'; + + +Learn more about SSL Monitors in [the SSL monitor overview](/detect/uptime-monitoring/ssl-monitors/overview). + + +Use SSL Monitors to verify the health and security posture of your TLS certificates. The examples below show how to configure monitoring for common scenarios. + + +Before creating SSL Monitors, ensure you have: + +- An initialized Checkly CLI project +- Network access to the HTTPS endpoint you want to monitor +- The hostname (and optionally port) of the target server + +For additional setup information, see [CLI overview](/cli/overview). + + + + +```ts Basic Example +import { Frequency, SslMonitor } from "checkly/constructs" + +new SslMonitor("homepage-ssl", { + name: "Homepage Certificate", + description: "Monitors the TLS certificate for `example.com`.", + frequency: Frequency.EVERY_1H, + request: { + hostname: "example.com", + sslConfig: { + alertDaysBeforeExpiry: 30, + }, + }, +}) +``` + +```ts Advanced Example +import { + Frequency, + SslAssertionBuilder, + SslMonitor, + TlsVersion, +} from "checkly/constructs" + +new SslMonitor("api-ssl-advanced", { + name: "API Certificate — strict baseline", + description: "Enforces TLS 1.3, checks chain trust, and alerts 45 days before expiry.", + activated: true, + frequency: Frequency.EVERY_10M, + locations: ["us-east-1", "eu-west-1"], + degradedResponseTime: 1000, + maxResponseTime: 5000, + request: { + hostname: "api.example.com", + port: 443, + sslConfig: { + alertDaysBeforeExpiry: 45, + handshakeTimeout: 5000, + securityBaseline: { + enabled: true, + minTLSVersion: { value: "TLS1.2", severity: "fail" }, + recommendedTLSVersion: { value: "TLS1.3", severity: "degrade" }, + weakCipherSuite: { severity: "fail" }, + ocspMustStapleRespected: { severity: "degrade" }, + }, + }, + assertions: [ + SslAssertionBuilder.certNotExpired().equals(true), + SslAssertionBuilder.chainTrusted().equals(true), + SslAssertionBuilder.hostnameVerified().equals(true), + SslAssertionBuilder.tlsVersion().greaterThanOrEqual(TlsVersion.TLS1_2), + SslAssertionBuilder.certExpiresInDays().greaterThan(45), + ], + }, +}) +``` + + + +## Configuration + +SSL monitors have SSL-specific settings and inherit the standard monitor options shared across all check types. + + + + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `request` | `object` | ✅ | - | SSL connection and certificate configuration | +| `degradedResponseTime` | `number` | ❌ | `3000` | Handshake time in milliseconds at which the monitor is marked degraded | +| `maxResponseTime` | `number` | ❌ | `10000` | Handshake time in milliseconds at which the monitor is marked failed | + + + + + + + + + +### `SslMonitor` Options + + + +SSL connection configuration that defines the target host and all TLS-specific options. + +**Usage:** + +```ts +new SslMonitor("my-ssl-monitor", { + name: "Example SSL Monitor", + request: { + hostname: "example.com", + sslConfig: { + alertDaysBeforeExpiry: 30, + }, + }, +}) +``` + +**Parameters:** + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `hostname` | `string` | ✅ | - | The hostname to connect to and validate (no scheme or port) | +| `port` | `number` | ❌ | `443` | TCP port to connect to (1–65535) | +| `ipFamily` | `string` | ❌ | `'IPv4'` | IP family: `'IPv4'` \| `'IPv6'` | +| `sslConfig` | `SslConfig` | ✅ | - | TLS handshake and certificate options (see below) | +| `assertions` | `SslAssertion[]` | ❌ | `[]` | Certificate/handshake assertions using `SslAssertionBuilder` | + + + + +TLS handshake time in milliseconds above which the monitor is marked as degraded (warning state). Must be ≤ `maxResponseTime`. + +**Usage:** + +```ts highlight={3} +new SslMonitor("my-ssl", { + name: "Example SSL", + degradedResponseTime: 1000, + maxResponseTime: 5000, + request: { + hostname: "example.com", + sslConfig: {}, + }, +}) +``` + + + +TLS handshake time in milliseconds above which the monitor is marked as failed. + +**Usage:** + +```ts highlight={4} +new SslMonitor("my-ssl", { + name: "Example SSL", + degradedResponseTime: 1000, + maxResponseTime: 5000, + request: { + hostname: "example.com", + sslConfig: {}, + }, +}) +``` + + +### `SslConfig` Options + + +Raise a degraded alert when the certificate is within this many days of expiry. Range: 1–365. + +```ts +sslConfig: { + alertDaysBeforeExpiry: 30, // Warn 30 days before expiry +} +``` + + + +SNI server name to send during the TLS handshake. Useful when a single IP hosts multiple certificates. Defaults to `hostname` when unset. + +```ts +sslConfig: { + serverName: "tenant-a.example.com", +} +``` + + + +Maximum milliseconds to wait for the TLS handshake to complete. Range: 1,000–30,000 ms. + +```ts +sslConfig: { + handshakeTimeout: 5000, +} +``` + + + +When `true`, the certificate chain is not verified against system trusted roots. The certificate is still inspected for expiry and the security baseline. Use for internal or self-signed certificates. + +```ts +sslConfig: { + skipChainValidation: true, +} +``` + + + +Enables mutual TLS by sending a client certificate during the handshake. + +- `'auto'` — Checkly selects a stored client certificate automatically. +- `'explicit'` — uses the certificate referenced by `sslClientCertificateId`. + +Omit to connect without a client certificate. + +```ts +sslConfig: { + clientCertificateMode: "explicit", + sslClientCertificateId: "cert_abc123", +} +``` + + + +Override the account-level security baseline for this monitor. Omit to inherit the account default. + +```ts +sslConfig: { + securityBaseline: { + enabled: true, + minTLSVersion: { value: "TLS1.2", severity: "fail" }, + recommendedTLSVersion: { value: "TLS1.3", severity: "degrade" }, + weakCipherSuite: { severity: "fail" }, + weakSignatureAlgorithm: { severity: "fail" }, + knownBadCA: { severity: "fail" }, + ocspMustStapleRespected: { severity: "degrade" }, + }, +} +``` + +**`SecurityBaseline` parameters:** + +| Parameter | Type | Default severity | Description | +|-----------|------|-----------------|-------------| +| `enabled` | `boolean` | `true` | Enable or disable baseline evaluation | +| `minTLSVersion` | `{ value?: string, severity? }` | `fail` | Minimum required TLS version (e.g. `'TLS1.2'`) | +| `minKeySizeBits` | `{ value?: number, severity? }` | `fail` | Minimum RSA key size in bits | +| `weakSignatureAlgorithm` | `{ severity? }` | `fail` | Reject weak signature algorithms (MD5, SHA-1, DSA-SHA1) | +| `weakCipherSuite` | `{ severity? }` | `fail` | Reject known-weak cipher suites | +| `knownBadCA` | `{ severity? }` | `fail` | Reject distrusted certificate authorities | +| `recommendedTLSVersion` | `{ value?: string, severity? }` | `ignore` | Advisory minimum TLS version | +| `recommendedKeySizeBits` | `{ value?: number, severity? }` | `ignore` | Advisory minimum RSA key size | +| `ocspMustStapleRespected` | `{ severity? }` | `ignore` | Alert when must-staple cert is missing an OCSP staple | +| `sctPresent` | `{ severity? }` | `ignore` | Alert when no Signed Certificate Timestamp is observed | + +Each rule's `severity` can be `'fail'` \| `'degrade'` \| `'ignore'`. + + +### `SslMonitor` Assertions + +Define `assertions` using the `SslAssertionBuilder`. The following sources are available: + +| Builder method | Source | Value type | Description | +|---------------|--------|-----------|-------------| +| `certNotExpired()` | `CERT_NOT_EXPIRED` | boolean | Certificate has not passed its expiry date | +| `certExpiresInDays()` | `CERT_EXPIRES_IN_DAYS` | number | Days until the certificate expires | +| `hostnameVerified()` | `HOSTNAME_VERIFIED` | boolean | Certificate SANs cover the monitored hostname | +| `chainTrusted()` | `CHAIN_TRUSTED` | boolean | Full chain is trusted by the system root store | +| `tlsVersion()` | `TLS_VERSION` | string | Negotiated TLS version | +| `cipherSuite()` | `CIPHER_SUITE` | string | Negotiated IANA cipher suite name | +| `signatureAlgorithm()` | `SIGNATURE_ALGORITHM` | string | Leaf certificate signature algorithm | +| `keySizeBits()` | `KEY_SIZE_BITS` | number | Leaf certificate public key size in bits | +| `issuerCn()` | `ISSUER_CN` | string | Common name of the certificate issuer | +| `certFingerprintSha256()` | `CERT_FINGERPRINT_SHA256` | string | SHA-256 fingerprint of the leaf certificate | +| `issuerFingerprintSha256()` | `ISSUER_FINGERPRINT_SHA256` | string | SHA-256 fingerprint of the issuer | +| `sanContains()` | `SAN_CONTAINS` | string | Any SAN matches the given value | +| `ocspStapled()` | `OCSP_STAPLED` | boolean | A stapled OCSP response was included in the handshake | +| `handshakeTimeMs()` | `HANDSHAKE_TIME_MS` | number | TLS handshake duration in milliseconds | + +Examples: + +```ts +// Certificate must not be expired +SslAssertionBuilder.certNotExpired().equals(true) +// Equivalent to: { source: 'CERT_NOT_EXPIRED', comparison: 'EQUALS', target: 'true' } + +// Alert when fewer than 30 days remain before expiry +SslAssertionBuilder.certExpiresInDays().greaterThan(30) +// Equivalent to: { source: 'CERT_EXPIRES_IN_DAYS', comparison: 'GREATER_THAN', target: '30' } + +// Require TLS 1.2 or newer +SslAssertionBuilder.tlsVersion().greaterThanOrEqual(TlsVersion.TLS1_2) +// Equivalent to: { source: 'TLS_VERSION', comparison: 'GREATER_THAN_OR_EQUAL', target: 'TLS1.2' } + +// Pin the cipher suite +SslAssertionBuilder.cipherSuite().equals(CipherSuite.TLS_AES_256_GCM_SHA384) +// Equivalent to: { source: 'CIPHER_SUITE', comparison: 'EQUALS', target: 'TLS_AES_256_GCM_SHA384' } + +// Verify a specific issuer +SslAssertionBuilder.issuerCn().equals("Let's Encrypt") +// Equivalent to: { source: 'ISSUER_CN', comparison: 'EQUALS', target: "Let's Encrypt" } + +// Require a minimum key size +SslAssertionBuilder.keySizeBits().greaterThanOrEqual(2048) +// Equivalent to: { source: 'KEY_SIZE_BITS', comparison: 'GREATER_THAN_OR_EQUAL', target: '2048' } +``` + +Use the `TlsVersion` and `CipherSuite` constants for type-safe comparisons: + +```ts +import { + CipherSuite, + SslAssertionBuilder, + TlsVersion, +} from "checkly/constructs" + +SslAssertionBuilder.tlsVersion().equals(TlsVersion.TLS1_3) +SslAssertionBuilder.cipherSuite().equals(CipherSuite.TLS_AES_128_GCM_SHA256) +``` + +Learn more in our docs on [Assertions](/detect/assertions). + +### General Monitor Options + + +Friendly name for your SSL Monitor, displayed in the Checkly dashboard and used in notifications. + +```ts highlight={2} +new SslMonitor("my-ssl-monitor", { + name: "Homepage Certificate", + /* More options ... */ +}) +``` + + + +How often the SSL Monitor should run. Use the `Frequency` enum to set the check interval. + +```ts highlight={3} +new SslMonitor("my-ssl-monitor", { + name: "Homepage Certificate", + frequency: Frequency.EVERY_1H, + /* More options ... */ +}) +``` + +**Available frequencies**: `EVERY_10S`, `EVERY_20S`, `EVERY_30S`, `EVERY_1M`, `EVERY_2M`, `EVERY_5M`, `EVERY_10M`, `EVERY_15M`, `EVERY_30M`, `EVERY_1H`, `EVERY_2H`, `EVERY_3H`, `EVERY_6H`, `EVERY_12H`, `EVERY_24H` + + + +Array of [public location codes](/concepts/locations/#public-locations) where the monitor should run from. Multiple locations provide geographic coverage. + +```ts highlight={3} +new SslMonitor("global-ssl", { + name: "Global Certificate Monitor", + locations: ["us-east-1", "eu-west-1", "ap-southeast-1"], + /* More options ... */ +}) +``` + + + +Whether the SSL Monitor is enabled and will run according to its schedule. + +```ts highlight={3} +new SslMonitor("my-ssl-monitor", { + name: "Homepage Certificate", + activated: false, // Disabled monitor + /* More options ... */ +}) +``` + + +## Examples + + + + ```ts + import { Frequency, SslMonitor } from "checkly/constructs" + + new SslMonitor("homepage-ssl", { + name: "Homepage Certificate", + frequency: Frequency.EVERY_1H, + locations: ["us-east-1", "eu-west-1"], + request: { + hostname: "example.com", + sslConfig: { + alertDaysBeforeExpiry: 30, + }, + }, + }) + ``` + + + + ```ts + import { Frequency, SslAssertionBuilder, SslMonitor, TlsVersion } from "checkly/constructs" + + new SslMonitor("api-strict-tls", { + name: "API — Strict TLS Policy", + frequency: Frequency.EVERY_10M, + locations: ["us-east-1", "eu-central-1"], + request: { + hostname: "api.example.com", + sslConfig: { + alertDaysBeforeExpiry: 45, + securityBaseline: { + enabled: true, + minTLSVersion: { value: "TLS1.2", severity: "fail" }, + recommendedTLSVersion: { value: "TLS1.3", severity: "degrade" }, + }, + }, + assertions: [ + SslAssertionBuilder.chainTrusted().equals(true), + SslAssertionBuilder.hostnameVerified().equals(true), + SslAssertionBuilder.tlsVersion().greaterThanOrEqual(TlsVersion.TLS1_2), + ], + }, + }) + ``` + + + + ```ts + import { Frequency, SslAssertionBuilder, SslMonitor } from "checkly/constructs" + + new SslMonitor("internal-ssl", { + name: "Internal Service Certificate", + frequency: Frequency.EVERY_6H, + privateLocations: ["my-private-location"], + request: { + hostname: "internal.corp.example.com", + port: 8443, + sslConfig: { + skipChainValidation: true, + alertDaysBeforeExpiry: 14, + }, + assertions: [ + SslAssertionBuilder.certNotExpired().equals(true), + SslAssertionBuilder.certExpiresInDays().greaterThan(7), + ], + }, + }) + ``` + + + + ```ts + import { Frequency, SslAssertionBuilder, SslMonitor } from "checkly/constructs" + + new SslMonitor("mtls-endpoint", { + name: "mTLS API Gateway", + frequency: Frequency.EVERY_5M, + request: { + hostname: "mtls.api.example.com", + sslConfig: { + clientCertificateMode: "explicit", + sslClientCertificateId: "cert_abc123", + alertDaysBeforeExpiry: 30, + }, + assertions: [ + SslAssertionBuilder.certNotExpired().equals(true), + SslAssertionBuilder.hostnameVerified().equals(true), + ], + }, + }) + ``` + + + + ```ts + import { Frequency, SslMonitor } from "checkly/constructs" + + // Monitor a certificate served for a specific SNI name + // on a shared IP (e.g. a CDN or multi-tenant server). + new SslMonitor("tenant-ssl", { + name: "Tenant Certificate via SNI", + frequency: Frequency.EVERY_1H, + request: { + hostname: "shared-cdn.example.net", + sslConfig: { + serverName: "tenant-a.example.com", + alertDaysBeforeExpiry: 20, + }, + }, + }) + ``` + + diff --git a/detect/uptime-monitoring/ssl-monitors/configuration.mdx b/detect/uptime-monitoring/ssl-monitors/configuration.mdx new file mode 100644 index 00000000..3e2a3f94 --- /dev/null +++ b/detect/uptime-monitoring/ssl-monitors/configuration.mdx @@ -0,0 +1,113 @@ +--- +title: 'SSL Monitor Configuration' +description: 'Configure your SSL monitor to track certificate health, expiry, and TLS security posture.' +sidebarTitle: 'Configuration' +--- + + +To configure an SSL monitor using code, learn more about the [SSL Monitor Construct](/constructs/ssl-monitor). + + +### Basic Setup + +Configure your SSL monitor by specifying the target host: + +* **Hostname:** The domain or IP address to connect to (e.g. `api.example.com`). Do not include a scheme or port. +* **Port:** The TCP port to connect to. Defaults to `443`. +* **IP family:** Choose between IPv4 (default) or IPv6. + +### SSL Configuration + +Fine-grained control over the TLS handshake behavior: + +* **SNI server name (`serverName`):** Override the Server Name Indication sent during the TLS handshake. Useful when a single IP serves multiple certificates and you want to request a specific one. Defaults to the configured hostname when unset. + +* **Handshake timeout:** Maximum time in milliseconds to wait for the TLS handshake to complete. Range: 1,000–30,000 ms. Default: 10,000 ms. + +* **Alert days before expiry (`alertDaysBeforeExpiry`):** Raise a degraded alert when the certificate is within this many days of expiry. Range: 1–365 days. Default: 20 days. The monitor always fails immediately if the certificate is already expired. + +* **Skip chain validation:** When enabled, Checkly does not verify the certificate chain against trusted roots. The certificate is still inspected for expiry and the security baseline. Use this when monitoring internal or self-signed certificates. + +### Client Certificate / mTLS + +SSL monitors can present a client certificate during the TLS handshake to verify mTLS-protected endpoints: + +* **`clientCertificateMode: 'auto'`** — Checkly automatically selects a stored client certificate for the target host. +* **`clientCertificateMode: 'explicit'`** — Use the specific certificate identified by `sslClientCertificateId`. + +Omit `clientCertificateMode` to connect without a client certificate (the default). + +Client certificates are stored at the account level under **Settings → Client Certificates**. + +### Security Baseline + +The security baseline is a configurable rule set that evaluates the negotiated TLS session against best-practice requirements. It runs automatically on every check and produces a **verdict** (`pass` / `warn` / `fail`) and a **grade** (`A-`, `B`, or `F`). + +You can override the default severity for each rule: + +| Rule | Default severity | Configurable | +|------|-----------------|:---:| +| `minTLSVersion` | `fail` | ✅ | +| `minKeySizeBits` | `fail` | ✅ | +| `weakSignatureAlgorithm` | `fail` | ✅ | +| `weakCipherSuite` | `fail` | ✅ | +| `knownBadCA` | `fail` | ✅ | +| `recommendedTLSVersion` | `ignore` | ✅ | +| `recommendedKeySizeBits` | `ignore` | ✅ | +| `ocspMustStapleRespected` | `ignore` | ✅ | +| `sctPresent` | `ignore` | ✅ | + +Each rule accepts a `severity` of `fail`, `degrade`, or `ignore`. Set `enabled: false` to disable the baseline entirely for a monitor. + + +Omitting the `securityBaseline` field inherits the account-level default baseline. Override it on a per-monitor basis only when you need non-standard thresholds for a specific endpoint. + + +### Assertions + +Use assertions to validate specific certificate and TLS handshake properties beyond what the security baseline covers. + +Available assertion sources: + +| Source | Type | Description | +|--------|------|-------------| +| `CERT_NOT_EXPIRED` | boolean | Certificate has not passed its `notAfter` date | +| `CERT_EXPIRES_IN_DAYS` | number | Days until the certificate expires | +| `HOSTNAME_VERIFIED` | boolean | The certificate's SANs cover the monitored hostname | +| `CHAIN_TRUSTED` | boolean | The full certificate chain is trusted by the system root store | +| `TLS_VERSION` | string | Negotiated TLS protocol version (e.g. `TLS1.2`, `TLS1.3`) | +| `CIPHER_SUITE` | string | IANA cipher suite name negotiated during the handshake | +| `SIGNATURE_ALGORITHM` | string | Leaf certificate signature algorithm (e.g. `SHA256-RSA`, `ECDSA-SHA256`) | +| `KEY_SIZE_BITS` | number | Leaf certificate public key size in bits | +| `ISSUER_CN` | string | Common name of the certificate issuer | +| `CERT_FINGERPRINT_SHA256` | string | SHA-256 fingerprint of the leaf certificate | +| `ISSUER_FINGERPRINT_SHA256` | string | SHA-256 fingerprint of the issuer certificate | +| `SAN_CONTAINS` | string | True when any Subject Alternative Name matches the given value | +| `OCSP_STAPLED` | boolean | A stapled OCSP response was included in the handshake | +| `HANDSHAKE_TIME_MS` | number | TLS handshake duration in milliseconds | + +For more details, see our documentation on [Assertions](/detect/assertions). + +### Response Time Limits + +Set performance thresholds based on the TLS handshake duration: + +* **Degraded After:** Handshake time (in milliseconds) above which the monitor is marked as degraded but not failed. Default: 3,000 ms. +* **Failed After:** Handshake time above which the monitor fails. Must be greater than or equal to the degraded threshold. Default: 10,000 ms. + +### Frequency + +Set how often the monitor runs (every 10 seconds to 24 hours). + +### Scheduling & Locations + +* **Strategy:** Choose between round-robin or parallel execution. Learn more about [scheduling strategies](/concepts/scheduling). +* **Locations:** Select [public](/concepts/locations/#public-locations) or [private](/platform/private-locations/overview) locations to run the monitor from. + +### Additional Settings + +* **Name:** Give your monitor a clear name to identify it in dashboards and alerts. +* **Description:** Add context about what this monitor does and why it matters. Supports markdown, max 500 characters. When a failure occurs, [Rocky AI](/ai/rocky-ai) uses the description to provide more accurate [root cause and user impact analysis](/resolve/ai-root-cause-analysis/overview). +* **Tags:** Use tags to organize monitors across [dashboards](/communicate/dashboards/overview/) and [maintenance windows](/communicate/maintenance-windows/overview). +* **Retries:** Define how failed runs should be retried. See [retry strategies](/communicate/alerts/retries). +* **Alerting:** Configure your [alert settings](/communicate/alerts/configuration), [alert channels](/communicate/alerts/channels), or set up [webhooks](/integrations/alerts/webhooks) for custom integrations. diff --git a/detect/uptime-monitoring/ssl-monitors/overview.mdx b/detect/uptime-monitoring/ssl-monitors/overview.mdx new file mode 100644 index 00000000..fcf9e79c --- /dev/null +++ b/detect/uptime-monitoring/ssl-monitors/overview.mdx @@ -0,0 +1,94 @@ +--- +title: 'SSL Monitor Overview' +description: 'Monitor TLS certificate health, expiry, and security posture of your HTTPS endpoints.' +sidebarTitle: Overview +--- + + +**Monitoring as Code**: Learn more about the [SSL Monitor Construct](/constructs/ssl-monitor). + + +## What are SSL Monitors? + +SSL monitors verify the health of a TLS certificate by connecting to a hostname, completing a full TLS handshake, and inspecting the resulting certificate and negotiated protocol parameters. Typical use cases include: + +* Detecting certificate expiry before it causes user-facing errors +* Verifying that a recently-renewed certificate is deployed to all your servers +* Enforcing minimum TLS version and cipher strength across your infrastructure +* Catching chain trust failures caused by missing intermediate certificates +* Alerting on weak or compromised certificate authority usage + +## How do SSL Monitors work? + +Each SSL monitor run performs the following steps: + +1. **DNS resolution** — If a hostname is provided, Checkly resolves it to an IP address +2. **TCP connect** — A TCP connection is opened to the target host and port (default: 443) +3. **TLS handshake** — A full TLS handshake is performed; an optional SNI server name override can be sent during this step +4. **Certificate inspection** — The leaf certificate and chain are examined: expiry date, hostname verification, chain trust, signature algorithm, key size, SANs, and fingerprints are all captured +5. **Security baseline evaluation** — A configurable rule set evaluates the negotiated protocol (TLS version, cipher suite, CA trust) and produces a per-rule verdict plus an overall **grade** and **verdict** (`pass` / `warn` / `fail`) +6. **Assertions** — Optional assertions are evaluated against the certificate and handshake data + +## SSL Monitor Results + +Select a specific check run to inspect its results: + +* **Summary:** Shows the target hostname and port, the monitor state (passed, degraded, or failed), and the TLS handshake time + +* **Error details:** If the run failed, the error category and message explain what went wrong (e.g., `EXPIRED_CERT`, `HOSTNAME_MISMATCH`, `HANDSHAKE_TIMEOUT`) + +* **Handshake data:** The negotiated TLS version, cipher suite, and handshake duration in milliseconds + +* **Certificate details:** Subject, issuer, serial number, validity window (not-before / not-after), days until expiry, signature algorithm, key algorithm and size, subject alternative names (SANs), and SHA-256 fingerprint + +* **Security baseline:** The per-rule verdict table and the aggregate grade (`A-`, `B`, or `F`) produced by the security baseline evaluation + +* **Certificate chain:** The intermediate and root certificates presented during the handshake, with their subjects, issuers, validity dates, and fingerprints + +Learn more in our documentation on [Results](/concepts/results). + +## The Security Baseline + +The security baseline is a built-in rule set that evaluates each handshake against current best practices without requiring explicit assertions. Rules cover: + +| Rule | Default severity | What it checks | +|------|-----------------|----------------| +| `minTLSVersion` | `fail` | Negotiated TLS version is at or above the required minimum | +| `minKeySizeBits` | `fail` | RSA key size meets the required minimum | +| `weakSignatureAlgorithm` | `fail` | No non-root certificate in the chain uses MD2, MD5, SHA-1, or DSA-SHA1 | +| `weakCipherSuite` | `fail` | The negotiated cipher suite is not in the known-weak list | +| `knownBadCA` | `fail` | No certificate in the chain was issued by a distrusted CA | +| `recommendedTLSVersion` | `ignore` (advisory) | Negotiated version meets the recommended minimum | +| `recommendedKeySizeBits` | `ignore` (advisory) | RSA key meets the advisory size recommendation | +| `ocspMustStapleRespected` | `ignore` (advisory) | If the leaf declares OCSP must-staple, a stapled response was provided | +| `sctPresent` | `ignore` (advisory) | A Signed Certificate Timestamp was observed in the handshake | + +The aggregate **grade** is computed from the worst rule outcome: + +| Grade | Meaning | +|-------|---------| +| `A-` | All rules pass (or baseline disabled) | +| `B` | At least one advisory or degrade-severity rule violated | +| `F` | At least one fail-severity rule violated | + +You can override the default severity for each rule or disable the baseline entirely in the [SSL Config](/detect/uptime-monitoring/ssl-monitors/configuration#security-baseline). + +## Troubleshooting Common Issues + + +This can happen when a server hosts multiple certificates and the default certificate served to Checkly does not cover the monitored hostname. Use the **SNI server name** (`serverName`) option in the SSL config to explicitly request the certificate for your hostname. + +If the certificate is legitimately shared across hostnames (e.g. a wildcard `*.example.com` that covers `api.example.com`) and hostname verification still fails, confirm the SAN list on the certificate contains the monitored hostname. + + + +A chain trust failure means one or more intermediate certificates are missing or the root is not trusted. Common causes: + +* The server is not sending the full intermediate chain — verify with `openssl s_client -connect hostname:443 -showcerts` +* The certificate was issued by a CA whose root was recently distrusted (e.g. Entrust 2024) +* Self-signed or internal CA certificates — enable **Skip chain validation** in the SSL config if you only want to monitor expiry and other properties without trust verification + + + +The `alertDaysBeforeExpiry` setting (default: 20 days) controls when a degraded alert is raised due to approaching expiry. If you use aggressive certificate rotation you may want to lower this value; if you need more lead time to trigger your renewal process, raise it. The monitor always fails immediately when the certificate is expired (`daysUntilExpiry < 0`), regardless of this setting. + diff --git a/docs.json b/docs.json index abfd76d5..3b885c5e 100644 --- a/docs.json +++ b/docs.json @@ -167,6 +167,13 @@ "detect/uptime-monitoring/icmp-monitors/configuration" ] }, + { + "group": "SSL Monitors", + "pages": [ + "detect/uptime-monitoring/ssl-monitors/overview", + "detect/uptime-monitoring/ssl-monitors/configuration" + ] + }, { "group": "Heartbeat Monitors", "pages": [ @@ -481,6 +488,7 @@ "constructs/dns-monitor", "constructs/tcp-monitor", "constructs/icmp-monitor", + "constructs/ssl-monitor", "constructs/heartbeat-monitor", "constructs/agentic-check", "constructs/api-check", From 4f2b51804e42a398b3cff86c53d3616d9754c281 Mon Sep 17 00:00:00 2001 From: Daniel Paulus Date: Wed, 8 Jul 2026 16:07:25 +0200 Subject: [PATCH 2/3] docs: fix SSL monitor accuracy issues from review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace fabricated failure-category strings (EXPIRED_CERT, HOSTNAME_MISMATCH, HANDSHAKE_TIMEOUT) with the six real runner categories: dns, connect, timeout, handshake, hostname, chain. Note that an expired cert is a failure state (daysUntilExpiry < 0), not a separate category. - Fix accordion titles to describe behavior rather than invented enums. - Clarify security baseline runs only on a successful handshake and certificate verification, not on hostname/chain failure. - Fix ">= threshold" wording: "above which" → "at or above which" for degradedResponseTime and maxResponseTime; add 0–30,000 ms range. - Add sslClientCertificateId as an explicit SslConfig ResponseField in the construct reference. - Expand weak-signature algorithm list to include MD2-RSA and ECDSA-SHA1 (matching baseline.go isWeakSignature). --- constructs/ssl-monitor.mdx | 17 ++++++++++++++--- .../ssl-monitors/configuration.mdx | 6 +++--- .../uptime-monitoring/ssl-monitors/overview.mdx | 16 ++++++++-------- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/constructs/ssl-monitor.mdx b/constructs/ssl-monitor.mdx index 2d1b2c8c..01eb6d79 100644 --- a/constructs/ssl-monitor.mdx +++ b/constructs/ssl-monitor.mdx @@ -137,7 +137,7 @@ new SslMonitor("my-ssl-monitor", { -TLS handshake time in milliseconds above which the monitor is marked as degraded (warning state). Must be ≤ `maxResponseTime`. +TLS handshake time in milliseconds at or above which the monitor is marked as degraded (warning state). Range: 0–30,000 ms. Must be ≤ `maxResponseTime`. **Usage:** @@ -155,7 +155,7 @@ new SslMonitor("my-ssl", { -TLS handshake time in milliseconds above which the monitor is marked as failed. +TLS handshake time in milliseconds at or above which the monitor is marked as failed. Range: 0–30,000 ms. **Usage:** @@ -230,6 +230,17 @@ sslConfig: { ``` + +The ID of the stored client certificate to present during the TLS handshake. Required when `clientCertificateMode` is `'explicit'`. Client certificates are managed under **Settings → Client Certificates** in the Checkly dashboard. + +```ts +sslConfig: { + clientCertificateMode: "explicit", + sslClientCertificateId: "cert_abc123", +} +``` + + Override the account-level security baseline for this monitor. Omit to inherit the account default. @@ -254,7 +265,7 @@ sslConfig: { | `enabled` | `boolean` | `true` | Enable or disable baseline evaluation | | `minTLSVersion` | `{ value?: string, severity? }` | `fail` | Minimum required TLS version (e.g. `'TLS1.2'`) | | `minKeySizeBits` | `{ value?: number, severity? }` | `fail` | Minimum RSA key size in bits | -| `weakSignatureAlgorithm` | `{ severity? }` | `fail` | Reject weak signature algorithms (MD5, SHA-1, DSA-SHA1) | +| `weakSignatureAlgorithm` | `{ severity? }` | `fail` | Reject weak signature algorithms (MD2-RSA, MD5-RSA, SHA1-RSA, DSA-SHA1, ECDSA-SHA1) on non-root certificates | | `weakCipherSuite` | `{ severity? }` | `fail` | Reject known-weak cipher suites | | `knownBadCA` | `{ severity? }` | `fail` | Reject distrusted certificate authorities | | `recommendedTLSVersion` | `{ value?: string, severity? }` | `ignore` | Advisory minimum TLS version | diff --git a/detect/uptime-monitoring/ssl-monitors/configuration.mdx b/detect/uptime-monitoring/ssl-monitors/configuration.mdx index 3e2a3f94..ca19f32f 100644 --- a/detect/uptime-monitoring/ssl-monitors/configuration.mdx +++ b/detect/uptime-monitoring/ssl-monitors/configuration.mdx @@ -41,7 +41,7 @@ Client certificates are stored at the account level under **Settings → Client ### Security Baseline -The security baseline is a configurable rule set that evaluates the negotiated TLS session against best-practice requirements. It runs automatically on every check and produces a **verdict** (`pass` / `warn` / `fail`) and a **grade** (`A-`, `B`, or `F`). +The security baseline is a configurable rule set that evaluates the negotiated TLS session against best-practice requirements. It runs on each successful handshake and certificate verification, and produces a **verdict** (`pass` / `warn` / `fail`) and a **grade** (`A-`, `B`, or `F`). It is skipped when an earlier step fails (e.g. hostname mismatch or chain untrusted). You can override the default severity for each rule: @@ -92,8 +92,8 @@ For more details, see our documentation on [Assertions](/detect/assertions). Set performance thresholds based on the TLS handshake duration: -* **Degraded After:** Handshake time (in milliseconds) above which the monitor is marked as degraded but not failed. Default: 3,000 ms. -* **Failed After:** Handshake time above which the monitor fails. Must be greater than or equal to the degraded threshold. Default: 10,000 ms. +* **Degraded After:** Handshake time (in milliseconds) at or above which the monitor is marked as degraded but not failed. Range: 0–30,000 ms. Default: 3,000 ms. +* **Failed After:** Handshake time at or above which the monitor fails. Must be greater than or equal to the degraded threshold. Range: 0–30,000 ms. Default: 10,000 ms. ### Frequency diff --git a/detect/uptime-monitoring/ssl-monitors/overview.mdx b/detect/uptime-monitoring/ssl-monitors/overview.mdx index fcf9e79c..13ef331e 100644 --- a/detect/uptime-monitoring/ssl-monitors/overview.mdx +++ b/detect/uptime-monitoring/ssl-monitors/overview.mdx @@ -26,7 +26,7 @@ Each SSL monitor run performs the following steps: 2. **TCP connect** — A TCP connection is opened to the target host and port (default: 443) 3. **TLS handshake** — A full TLS handshake is performed; an optional SNI server name override can be sent during this step 4. **Certificate inspection** — The leaf certificate and chain are examined: expiry date, hostname verification, chain trust, signature algorithm, key size, SANs, and fingerprints are all captured -5. **Security baseline evaluation** — A configurable rule set evaluates the negotiated protocol (TLS version, cipher suite, CA trust) and produces a per-rule verdict plus an overall **grade** and **verdict** (`pass` / `warn` / `fail`) +5. **Security baseline evaluation** — On a successful handshake and certificate verification, a configurable rule set evaluates the negotiated protocol (TLS version, cipher suite, CA trust) and produces a per-rule verdict plus an overall **grade** and **verdict** (`pass` / `warn` / `fail`). The baseline is skipped when an earlier step fails (e.g. hostname mismatch or chain untrusted). 6. **Assertions** — Optional assertions are evaluated against the certificate and handshake data ## SSL Monitor Results @@ -35,7 +35,7 @@ Select a specific check run to inspect its results: * **Summary:** Shows the target hostname and port, the monitor state (passed, degraded, or failed), and the TLS handshake time -* **Error details:** If the run failed, the error category and message explain what went wrong (e.g., `EXPIRED_CERT`, `HOSTNAME_MISMATCH`, `HANDSHAKE_TIMEOUT`) +* **Error details:** If the run failed, the error category and message explain what went wrong. Categories map to the stage where the failure occurred: `dns` (name resolution), `connect` (TCP connection), `timeout` or `handshake` (TLS handshake), `hostname` (certificate does not cover the monitored hostname), or `chain` (certificate chain not trusted). An expired certificate surfaces as a failure via a negative `daysUntilExpiry` value rather than a separate category. * **Handshake data:** The negotiated TLS version, cipher suite, and handshake duration in milliseconds @@ -49,13 +49,13 @@ Learn more in our documentation on [Results](/concepts/results). ## The Security Baseline -The security baseline is a built-in rule set that evaluates each handshake against current best practices without requiring explicit assertions. Rules cover: +The security baseline is a built-in rule set that evaluates each successful handshake and certificate verification against current best practices without requiring explicit assertions. Rules cover: | Rule | Default severity | What it checks | |------|-----------------|----------------| | `minTLSVersion` | `fail` | Negotiated TLS version is at or above the required minimum | | `minKeySizeBits` | `fail` | RSA key size meets the required minimum | -| `weakSignatureAlgorithm` | `fail` | No non-root certificate in the chain uses MD2, MD5, SHA-1, or DSA-SHA1 | +| `weakSignatureAlgorithm` | `fail` | No non-root certificate in the chain uses MD2-RSA, MD5-RSA, SHA1-RSA, DSA-SHA1, or ECDSA-SHA1 | | `weakCipherSuite` | `fail` | The negotiated cipher suite is not in the known-weak list | | `knownBadCA` | `fail` | No certificate in the chain was issued by a distrusted CA | | `recommendedTLSVersion` | `ignore` (advisory) | Negotiated version meets the recommended minimum | @@ -75,14 +75,14 @@ You can override the default severity for each rule or disable the baseline enti ## Troubleshooting Common Issues - -This can happen when a server hosts multiple certificates and the default certificate served to Checkly does not cover the monitored hostname. Use the **SNI server name** (`serverName`) option in the SSL config to explicitly request the certificate for your hostname. + +This can happen when a server hosts multiple certificates and the default certificate served to Checkly does not cover the monitored hostname. The runner reports this as failure category `hostname`. Use the **SNI server name** (`serverName`) option in the SSL config to explicitly request the certificate for your hostname. If the certificate is legitimately shared across hostnames (e.g. a wildcard `*.example.com` that covers `api.example.com`) and hostname verification still fails, confirm the SAN list on the certificate contains the monitored hostname. - -A chain trust failure means one or more intermediate certificates are missing or the root is not trusted. Common causes: + +A chain trust failure (failure category `chain`) means one or more intermediate certificates are missing or the root is not trusted. Common causes: * The server is not sending the full intermediate chain — verify with `openssl s_client -connect hostname:443 -showcerts` * The certificate was issued by a CA whose root was recently distrusted (e.g. Entrust 2024) From 0bf7c0227a8211e2e52ca5276166877e4cbfe6d4 Mon Sep 17 00:00:00 2001 From: Daniel Paulus Date: Wed, 8 Jul 2026 16:45:04 +0200 Subject: [PATCH 3/3] docs: correct SSL monitor minimum frequency to 1 minute (SIM-283) SSL monitors do not offer sub-minute frequencies; the floor is 1 minute, not 10 seconds. --- constructs/ssl-monitor.mdx | 2 +- detect/uptime-monitoring/ssl-monitors/configuration.mdx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/constructs/ssl-monitor.mdx b/constructs/ssl-monitor.mdx index 01eb6d79..20d583e0 100644 --- a/constructs/ssl-monitor.mdx +++ b/constructs/ssl-monitor.mdx @@ -364,7 +364,7 @@ new SslMonitor("my-ssl-monitor", { }) ``` -**Available frequencies**: `EVERY_10S`, `EVERY_20S`, `EVERY_30S`, `EVERY_1M`, `EVERY_2M`, `EVERY_5M`, `EVERY_10M`, `EVERY_15M`, `EVERY_30M`, `EVERY_1H`, `EVERY_2H`, `EVERY_3H`, `EVERY_6H`, `EVERY_12H`, `EVERY_24H` +**Available frequencies**: `EVERY_1M`, `EVERY_2M`, `EVERY_5M`, `EVERY_10M`, `EVERY_15M`, `EVERY_30M`, `EVERY_1H`, `EVERY_2H`, `EVERY_3H`, `EVERY_6H`, `EVERY_12H`, `EVERY_24H`. SSL monitors do not support sub-minute frequencies (`EVERY_10S` / `EVERY_20S` / `EVERY_30S`). diff --git a/detect/uptime-monitoring/ssl-monitors/configuration.mdx b/detect/uptime-monitoring/ssl-monitors/configuration.mdx index ca19f32f..12a91566 100644 --- a/detect/uptime-monitoring/ssl-monitors/configuration.mdx +++ b/detect/uptime-monitoring/ssl-monitors/configuration.mdx @@ -97,7 +97,7 @@ Set performance thresholds based on the TLS handshake duration: ### Frequency -Set how often the monitor runs (every 10 seconds to 24 hours). +Set how often the monitor runs. SSL monitors run at most **once per minute** (every 1 minute to 24 hours) — sub-minute frequencies aren't available, since certificate state changes slowly. ### Scheduling & Locations