TLS Certificate Check¶
Version: v1
Kind: TlsCheck
Alias: SslCheck (for backward compatibility)
Status: Stable
Overview¶
The TLS Certificate Check validates TLS/SSL certificates for any TCP service, not just HTTPS. It monitors certificate validity, expiration, and issuer information.
Common Use Cases: - HTTPS servers (port 443) - SMTP with STARTTLS (port 587) or SMTPS (port 465) - IMAP with SSL/TLS (port 993) - POP3 with SSL/TLS (port 995) - PostgreSQL with TLS (port 5432) - MySQL with TLS (port 3306) - LDAPS (port 636) - Any TCP service using TLS
Naming: TlsCheck vs SslCheck¶
Primary Name: TlsCheck (technically correct, protocol-agnostic)
Alias: SslCheck (widely recognized, user-friendly)
Both names are accepted in YAML definitions and resolve to the same implementation:
Rationale: - TLS is the modern protocol (SSL is deprecated since 2015) - "SSL" terminology remains widely used in industry - Supporting both names provides technical accuracy AND user familiarity - TlsCheck is protocol-agnostic (not web-specific like "SSL certificate")
Resource Structure¶
apiVersion: v1
kind: TlsCheck # or SslCheck
metadata:
name: {check-name}
labels:
key: value
spec:
hostname: example.com
port: 443 # optional, default: 443
# Custom CA trust (OPTIONAL - for internal PKI)
trustedCAs:
- |
-----BEGIN CERTIFICATE-----
MIIDXTCCAkWgAwIBAgIJAKJ...
-----END CERTIFICATE-----
# OR skip verification (OPTIONAL - development only!)
# insecureSkipVerify: true
# Scheduling (REQUIRED - one of)
interval: 1h
# OR
cron: "0 */6 * * *"
# Common fields (OPTIONAL)
timeout: 10s
retries: 3
locations:
- us-east-1
channels:
- channel: alerts
severity: High
# Assertions (REQUIRED)
checks:
- type: expirationTime
operator: greaterThan
value: 30d
Spec Fields¶
hostname¶
Type: DNSHostname
REQUIRED
Description: The hostname of the server whose certificate to validate.
Examples:
Validation: - MUST be valid DNS hostname - Automatically converted to lowercase - Maximum length: 253 characters
port¶
Type: integer
OPTIONAL
Default: 443
Description: The TCP port where the TLS service is running.
Common Ports:
- 443 - HTTPS (default)
- 465 - SMTPS (SMTP over SSL)
- 587 - SMTP with STARTTLS
- 993 - IMAPS (IMAP over SSL)
- 995 - POP3S (POP3 over SSL)
- 636 - LDAPS (LDAP over SSL)
- 5432 - PostgreSQL with TLS
- 3306 - MySQL with TLS
Examples:
Validation: - MUST be integer between 1 and 65535
trustedCAs¶
Type: array[string]
OPTIONAL
Default: null (use system trust store)
Description: Custom CA certificates to trust for certificate validation. If specified, replaces the system trust store.
Format: Array of PEM-encoded X.509 certificates (string format).
Examples:
Single internal CA:
Multiple CAs (internal PKI hierarchy):
trustedCAs:
- |
-----BEGIN CERTIFICATE-----
... Root CA ...
-----END CERTIFICATE-----
- |
-----BEGIN CERTIFICATE-----
... Intermediate CA ...
-----END CERTIFICATE-----
Semantics: - If NOT specified: Use system trust store (default behavior) - If specified: Use ONLY these CAs (system trust store is ignored) - Validates certificate chain against provided CAs - Still checks: expiration, hostname match, revocation (if enabled)
Use Cases: - Internal networks: Services with internal CA-issued certificates - Private PKI: Enterprise infrastructure with private certificate authorities - Air-gapped environments: No access to public CA infrastructure - Testing: Validate against test CAs without modifying system trust store
Security Considerations:
- ⚠️ Validate CA certificates before adding to trustedCAs
- ⚠️ Store securely: CAs are sensitive (control what you trust)
- ⚠️ Audit regularly: Review which checks use custom CAs
- ⚠️ Rotate CAs: Update when CAs expire or are compromised
- ✅ Prefer proper PKI: Better than self-signed certificates
Validation:
- Each CA MUST be valid PEM-encoded X.509 certificate
- Mutually exclusive with insecureSkipVerify
Best Practices: - Use for production internal services with proper PKI - Document why custom CAs are needed - Maintain CA certificate lifecycle (expiration, rotation) - Store CA certificates in secrets management system - Don't commit CA certificates to public repositories
insecureSkipVerify¶
Type: boolean
OPTIONAL
Default: false
Description: Skip ALL certificate validation. ⚠️ DEVELOPMENT/TESTING ONLY - disables trust verification.
Examples:
Development with self-signed certificate:
Semantics:
- If false (default): Perform full certificate validation
- If true: Skip certificate validation entirely
- No expiration check
- No trust chain validation
- No hostname verification
- No revocation check
- Still performs TLS handshake (connection is encrypted)
- Incompatible with valid assertion (validation disabled)
- Can still use: expirationTime, certificateIssuer, certificateSubject
Use Cases: - ⚠️ Development: Local testing with self-signed certificates - ⚠️ Debugging: Investigate TLS issues - ⚠️ Migration: Temporary workaround during certificate migration - ❌ Production: NEVER use in production environments
Security Warnings:
# ⚠️⚠️⚠️ SECURITY WARNING ⚠️⚠️⚠️
# insecureSkipVerify disables ALL certificate validation
#
# This makes you vulnerable to:
# - Man-in-the-middle attacks
# - Impersonation attacks
# - Expired certificate usage
# - Untrusted certificate acceptance
#
# Use ONLY for development/testing
# NEVER use in production
insecureSkipVerify: true
Validation:
- Mutually exclusive with trustedCAs
- Incompatible with valid assertion type
- If true and checks contains valid, validation FAILS
Examples of Invalid Usage:
# ❌ INVALID: Cannot validate when verification skipped
spec:
hostname: localhost
insecureSkipVerify: true
checks:
- type: valid # ERROR: incompatible
operator: is
value: true
# ❌ INVALID: Mutually exclusive with trustedCAs
spec:
hostname: example.com
trustedCAs: [...]
insecureSkipVerify: true # ERROR: conflicting
Valid Development Usage:
# ✅ VALID: Can check expiration even without trust validation
spec:
hostname: localhost
port: 8443
insecureSkipVerify: true
checks:
- type: expirationTime
operator: greaterThan
value: 7d
Alternatives:
- Production: Use trustedCAs with proper internal CA
- Development: Set up local CA and use trustedCAs
- Testing: Use mkcert or similar tools for trusted local certificates
checks¶
Type: array[TlsAssertion]
REQUIRED
Description: List of assertions to evaluate against the TLS certificate.
Supported Assertion Types:
1. expirationTime - Time until certificate expiration
2. certificateIssuer - Certificate issuer organization
3. certificateSubject - Certificate subject/CN
4. valid - Certificate validity (not expired, trusted, hostname match)
See Assertions section for details.
Assertions¶
All TLS assertions validate properties of the TLS certificate.
expirationTime¶
Type: TimebasedAssertion
Description: Assert on time until certificate expiration.
Fields:
- type: "expirationTime" (REQUIRED)
- operator: NumericOperator (REQUIRED)
- equals, notEquals, greaterThan, lessThan
- value: StrictTime (REQUIRED)
- MUST include unit suffix: "30d", "7d", "720h", etc.
- Bare integers are NOT allowed
Examples:
Alert if certificate expires in less than 30 days:
Alert if certificate expires in less than 7 days:
Using hours instead of days:
Using weeks:
Using calendar-based months (recommended for certificates):
Using calendar-based years:
Invalid - bare integers rejected:
Semantics:
- Value represents duration UNTIL expiration
- Check FAILS if certificate expires within the specified time
- greaterThan typically used to ensure sufficient validity remaining
- Unit suffix REQUIRED to avoid ambiguity (days vs seconds vs hours)
certificateIssuer¶
Type: StringAssertion
Description: Assert on the certificate issuer (CA) organization name.
Fields:
- type: "certificateIssuer" (REQUIRED)
- operator: StringOperator (REQUIRED)
- equals, notEquals, contains, notContains
- value: string (REQUIRED)
Examples:
Verify certificate issued by Let's Encrypt:
Verify certificate issued by specific CA:
Ensure certificate NOT issued by untrusted CA:
Common Issuers:
- "Let's Encrypt"
- "DigiCert Inc"
- "Google Trust Services"
- "Amazon"
- "Cloudflare"
- "GlobalSign"
certificateSubject¶
Type: StringAssertion
Description: Assert on the certificate subject (Common Name or Subject Alternative Names).
Fields:
- type: "certificateSubject" (REQUIRED)
- operator: StringOperator (REQUIRED)
- equals, notEquals, contains, notContains
- value: string (REQUIRED)
Examples:
Verify certificate subject contains domain:
Verify exact subject match:
- type: certificateSubject
operator: equals
value: "CN=*.example.com, O=Example Inc, L=San Francisco, ST=California, C=US"
Verify wildcard certificate:
Common Patterns:
- "CN=example.com" - Single domain
- "CN=*.example.com" - Wildcard certificate
- "O=Example Inc" - Organization
- "C=US" - Country
valid¶
Type: BooleanAssertion
Description: Assert on overall certificate validity.
Fields:
- type: "valid" (REQUIRED)
- operator: BooleanOperator (REQUIRED)
- is, isNot
- value: boolean (REQUIRED)
- true - Certificate should be valid
- false - Certificate should be invalid
Examples:
Assert certificate is valid:
Verify certificate is trusted:
Semantics:
- true means certificate passed all validation checks:
- Not expired (current time between notBefore and notAfter)
- Trusted certificate chain (signed by trusted CA)
- Hostname matches (certificate subject/SAN matches spec.hostname)
- Not revoked (implementation-dependent, may check OCSP/CRL)
- false means certificate failed validation
- Check PASSES if assertion evaluates to true
- Check FAILS if assertion evaluates to false
Validation Checks (implementation performs):
1. Expiration: Certificate not expired
2. Trust Chain: Certificate chain validates to trusted root CA
3. Hostname: Certificate subject/SAN matches spec.hostname
4. Revocation: Certificate not revoked (optional, implementation-dependent)
Use Cases: - Security compliance: Ensure certificates are properly validated - Trust validation: Verify certificates signed by trusted CAs - Hostname verification: Confirm certificate matches expected hostname - General health: Single assertion for overall certificate validity
Comparison with expirationTime:
- valid checks multiple criteria (expiration + trust + hostname)
- expirationTime only checks expiration date
- Use valid for general security compliance
- Use expirationTime for specific expiration monitoring/alerting
Examples:
Basic validity check:
Combined with expiration monitoring:
# General validity (security)
- type: valid
operator: is
value: true
# Specific expiration alert (30 day warning)
- type: expirationTime
operator: greaterThan
value: 30d
Examples¶
Basic HTTPS Certificate Check¶
apiVersion: v1
kind: TlsCheck
metadata:
name: web-tls-monitor
title: "HTTPS Certificate Monitor"
labels:
environment: production
spec:
hostname: example.com
port: 443
interval: 6h
timeout: 10s
retries: 2
checks:
- type: expirationTime
operator: greaterThan
value: 30d
- type: certificateIssuer
operator: contains
value: "Let's Encrypt"
locations:
- us-east-1
- eu-west-1
channels:
- channel: security-alerts
severity: Critical
SMTP TLS Certificate Check¶
apiVersion: v1
kind: TlsCheck
metadata:
name: smtp-tls-certificate
title: "SMTP Server TLS Monitor"
labels:
environment: production
service: email
spec:
hostname: smtp.example.com
port: 587 # SMTP with STARTTLS
interval: 6h
timeout: 5s
retries: 2
checks:
- type: expirationTime
operator: greaterThan
value: 7d
- type: certificateIssuer
operator: contains
value: "DigiCert"
locations:
- us-east-1
channels:
- channel: email-alerts
severity: High
Database TLS Certificate Check¶
apiVersion: v1
kind: TlsCheck
metadata:
name: postgres-tls-certificate
title: "PostgreSQL TLS Certificate Monitor"
labels:
environment: production
service: database
spec:
hostname: db.example.com
port: 5432
interval: 12h
timeout: 5s
checks:
- type: expirationTime
operator: greaterThan
value: 14d
- type: certificateSubject
operator: contains
value: "CN=db.example.com"
locations:
- us-east-1
channels:
- channel: database-alerts
severity: Critical
Using SslCheck Alias¶
apiVersion: v1
kind: SslCheck # Alias for TlsCheck
metadata:
name: ssl-cert-monitor
title: "SSL Certificate Monitor"
spec:
hostname: example.com
interval: 1h
checks:
- type: expirationTime
operator: greaterThan
value: 30d
Comprehensive Certificate Validation¶
apiVersion: v1
kind: TlsCheck
metadata:
name: comprehensive-tls-check
title: "Comprehensive TLS Validation"
labels:
environment: production
criticality: high
spec:
hostname: api.example.com
port: 443
interval: 6h
timeout: 10s
retries: 3
checks:
# Overall validity check
- type: valid
operator: is
value: true
# Ensure at least 30 days until expiration
- type: expirationTime
operator: greaterThan
value: 30d
# Verify trusted CA
- type: certificateIssuer
operator: contains
value: "Let's Encrypt"
# Verify certificate subject
- type: certificateSubject
operator: contains
value: "CN=api.example.com"
# Ensure not using wildcard (if desired)
- type: certificateSubject
operator: notContains
value: "*"
locations:
- us-east-1
- eu-west-1
- ap-southeast-1
channels:
- channel: pagerduty-security
severity: Critical
- channel: slack-security
severity: High
Internal Service with Custom CA¶
apiVersion: v1
kind: TlsCheck
metadata:
name: internal-api-tls
title: "Internal API TLS (Custom CA)"
labels:
environment: production
network: internal
spec:
hostname: internal-api.corp.example.com
port: 443
interval: 6h
timeout: 10s
# Custom internal CA certificate
trustedCAs:
- |
-----BEGIN CERTIFICATE-----
MIIDXTCCAkWgAwIBAgIJAKJ8FqEVUvKCMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMSEwHwYDVQQKDBhJbnRlcm5hbCBD
QSAtIEV4YW1wbGUgSW5jMB4XDTI0MDEwMTAwMDAwMFoXDTM0MDEwMTAwMDAwMFow
RTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExITAfBgNVBAoMGElu
dGVybmFsIENBIC0gRXhhbXBsZSBJbmMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
ggEKAoIBAQC...
-----END CERTIFICATE-----
checks:
# Validate against internal CA
- type: valid
operator: is
value: true
# Monitor expiration (30 day warning)
- type: expirationTime
operator: greaterThan
value: 30d
# Verify internal CA
- type: certificateIssuer
operator: contains
value: "Internal CA - Example Inc"
locations:
- internal-dc-1
channels:
- channel: internal-alerts
severity: High
Internal Service with Multiple CAs (PKI Hierarchy)¶
apiVersion: v1
kind: TlsCheck
metadata:
name: internal-db-tls
title: "Internal Database TLS (PKI Hierarchy)"
spec:
hostname: db.internal.example.com
port: 5432
interval: 12h
timeout: 5s
# Internal PKI - Root + Intermediate CAs
trustedCAs:
# Root CA
- |
-----BEGIN CERTIFICATE-----
MIIDXTCCAkWgAwIBAgIJAKJ8FqEVUvKCMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
... Root CA Certificate ...
-----END CERTIFICATE-----
# Intermediate CA
- |
-----BEGIN CERTIFICATE-----
MIIDXTCCAkWgAwIBAgIJAKJ8FqEVUvKDMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
... Intermediate CA Certificate ...
-----END CERTIFICATE-----
checks:
- type: valid
operator: is
value: true
- type: expirationTime
operator: greaterThan
value: 14d
locations:
- internal-dc-1
channels:
- channel: database-alerts
severity: Critical
Development with Self-Signed Certificate¶
apiVersion: v1
kind: TlsCheck
metadata:
name: dev-localhost-tls
title: "Development Localhost TLS"
labels:
environment: development
spec:
hostname: localhost
port: 8443
interval: 5m
timeout: 5s
# ⚠️ WARNING: Development only - disables validation
insecureSkipVerify: true
checks:
# Can still check expiration without trust validation
- type: expirationTime
operator: greaterThan
value: 7d
# Can check issuer/subject
- type: certificateSubject
operator: contains
value: "localhost"
locations:
- local
Testing Environment with Self-Signed (Better Approach)¶
apiVersion: v1
kind: TlsCheck
metadata:
name: staging-api-tls
title: "Staging API TLS (Test CA)"
labels:
environment: staging
spec:
hostname: api.staging.example.com
port: 443
interval: 1h
timeout: 10s
# Better than insecureSkipVerify: Use test CA
trustedCAs:
- |
-----BEGIN CERTIFICATE-----
... Test/Staging CA Certificate ...
-----END CERTIFICATE-----
checks:
- type: valid
operator: is
value: true
- type: expirationTime
operator: greaterThan
value: 30d
locations:
- staging-us-east-1
Validation Rules¶
Required Fields¶
apiVersionMUST be"v1"kindMUST be"TlsCheck"or"SslCheck"metadata.nameMUST be validCaseInsensitiveKeyspec.hostnameMUST be validDNSHostnamespec.checksMUST be non-empty array- One of
spec.intervalorspec.cronMUST be specified
Optional Fields¶
spec.portdefaults to443spec.trustedCAsdefaults tonull(use system trust store)spec.insecureSkipVerifydefaults tofalsespec.timeoutdefaults to1sspec.retriesdefaults to1spec.locationsdefaults to[]spec.channelsdefaults to[]
Field Constraints¶
spec.trustedCAsandspec.insecureSkipVerifyare mutually exclusive- If
spec.insecureSkipVerifyistrue,checksMUST NOT containvalidassertion - Each CA in
spec.trustedCAsMUST be valid PEM-encoded X.509 certificate
Extra Fields¶
- Extra fields are FORBIDDEN (
extra="forbid") - Unknown fields MUST cause validation errors
Execution Semantics¶
Connection Behavior¶
- Runner connects to
hostname:portvia TCP - Runner initiates TLS handshake
- Runner retrieves server certificate
- Runner evaluates all assertions against certificate
- Check PASSES if all assertions pass
- Check FAILS if any assertion fails
Error Conditions¶
Check FAILS if:
- Cannot connect to hostname:port
- TLS handshake fails
- No certificate presented
- Certificate is invalid or expired
- Any assertion evaluates to false
- Timeout exceeded
Retry Behavior¶
- Failed checks are retried up to
retriestimes - Retries occur immediately after failure
- If ANY attempt succeeds, check is considered successful
- Timeout applies to total execution time including retries
Conformance¶
Implementations claiming TlsCheck conformance MUST:
- Support both names: Accept
TlsCheckandSslCheckas valid kinds - Validate hostname: Enforce
DNSHostnameconstraints - Support all assertions: Implement
expirationTime,certificateIssuer,certificateSubject - Default port: Use
443when port is not specified - Evaluate all checks: Execute all assertions in
checksarray - Fail on any assertion failure: Check fails if ANY assertion fails
- Handle timeouts: Respect
timeoutfield - Retry on failure: Implement retry logic according to
retriesfield
Implementations SHOULD: - Support multiple locations - Send notifications to configured channels - Provide detailed error messages for validation failures - Report certificate details in results (issuer, subject, expiration date)
Security Considerations¶
Certificate Validation¶
- Implementations SHOULD validate entire certificate chain
- Implementations SHOULD verify certificate against system trust store
- Implementations MAY allow custom CA certificates
- Implementations SHOULD report certificate chain issues
Hostname Verification¶
- Implementations SHOULD verify certificate hostname matches
spec.hostname - Implementations SHOULD support wildcards and Subject Alternative Names (SANs)
- Implementations SHOULD report hostname mismatch as check failure
Timing Attacks¶
- Implementations SHOULD NOT leak timing information that could aid attackers
- Certificate validation SHOULD use constant-time operations where possible
Implementation Notes¶
Certificate Parsing¶
- Extract certificate expiration date
- Parse issuer Distinguished Name (DN)
- Parse subject Distinguished Name (DN)
- Handle certificate chain traversal
Expiration Calculation¶
- Calculate days until expiration:
(expiration_date - current_date).days - Use system clock or NTP-synchronized time
- Consider timezone differences
String Matching¶
- Certificate issuer/subject matching is case-sensitive by default
- Implementations MAY support case-insensitive matching
containsoperator searches anywhere in the DN string
Custom CA Trust Store¶
Check-Level Configuration (via trustedCAs):
- Specified in check definition
- Portable across runners
- Self-contained
- Per-check granularity
Runner-Level Configuration (alternative approach):
Implementations MAY support runner-level CA configuration via:
Environment Variables:
# Set custom CA bundle for all TLS checks
export CUSTOM_CA_BUNDLE=/etc/ssl/certs/internal-ca-bundle.pem
synthetic-runner start
# Or for multiple CAs
export CUSTOM_CA_PATH=/etc/ssl/certs/internal-cas/
synthetic-runner start
Configuration File:
# runner-config.yaml
tls:
customCABundle: /etc/ssl/certs/internal-ca-bundle.pem
# OR
customCAPath: /etc/ssl/certs/internal-cas/
# OR
trustedCAs:
- /etc/ssl/certs/ca1.pem
- /etc/ssl/certs/ca2.pem
System Trust Store Updates:
# Add CA to system trust store (Linux)
sudo cp internal-ca.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates
# Add CA to system trust store (macOS)
sudo security add-trusted-cert -d -r trustRoot \
-k /Library/Keychains/System.keychain internal-ca.crt
Precedence (when both check-level and runner-level configured):
1. Check-level trustedCAs (highest priority)
2. Check-level insecureSkipVerify
3. Runner-level CA configuration
4. System trust store (default)
Trade-offs:
| Approach | Pros | Cons |
|---|---|---|
Check-level (trustedCAs) |
Portable, explicit, auditable | Large YAML, duplication |
| Runner-level (env/config) | Shared across checks, smaller YAML | Implicit, runner-specific |
| System trust store | Standard approach, no config | Global impact, requires admin |
Recommendation:
- Production: Check-level trustedCAs (explicit, auditable)
- Development: Runner-level or system trust store (convenience)
- CI/CD: Runner-level via environment variables (flexibility)
Related¶
- Base Check Definition - Common check structure
- Common Types - Shared types and operators
- HttpCheck - HTTP endpoint checks (uses TLS on port 443)
Changelog¶
v1 (Current)¶
- Renamed from
SslChecktoTlsCheck(canonical name) - Added
SslCheckas backward-compatible alias - Four assertion types: expirationTime, certificateIssuer, certificateSubject, valid
- Added
validassertion for overall certificate validity checking - Added
trustedCAsfield for custom CA certificates (internal PKI support) - Added
insecureSkipVerifyfield for development/testing (disables validation) - Clarified support for non-web protocols (SMTP, databases, etc.)
- Added port examples for common TLS services
- Documented both
TlsCheckandSslCheckas valid kinds - Documented runner-level CA configuration options
- StrictTime for expirationTime assertions (unit suffix required)