Files
cleveragents-core/features/tls_certificate_check.feature
freemo 8c81f13758 fix(infra): resolve TLS handshake failure on git.dev.cleveragents.com
The TLS handshake failure on git.dev.cleveragents.com was caused by the
hostname being absent from the certificate's Subject Alternative Names
(SANs), or by SNI virtual-host misconfiguration on the server side.

This commit delivers the repository-side remediation:

- scripts/check-tls-cert.py: New TLS certificate health-check script.
  Connects to a hostname, verifies the certificate's SANs include the
  target hostname, checks expiry, and reports errors/warnings.  Accepts
  an injectable SSLContext for unit testing without real network access.
  Supports wildcard SAN matching and configurable expiry warning threshold.

- docs/development/ops-runbook.md: New ops runbook documenting the full
  certificate renewal procedure (Let's Encrypt/certbot and manual CA),
  SNI misconfiguration diagnosis steps, expiry monitoring with cron, and
  recommended alert thresholds (30/14/7/0 days).

- features/tls_certificate_check.feature: 14 Behave scenarios tagged
  @tdd_issue @tdd_issue_1543 covering: missing SAN detection, valid SAN
  acceptance, expired certificate detection, expiry warning threshold,
  TLS handshake errors, connection timeouts, connection refused, wildcard
  SAN matching, and _hostname_matches_san unit tests.

- features/steps/tls_certificate_check_steps.py: Step definitions for
  the above feature, using unittest.mock to inject SSL contexts and
  socket connections so no real network calls are made.

- mkdocs.yml: Added Ops Runbook to the Development section navigation.

The actual server-side certificate renewal (adding git.dev.cleveragents.com
as a SAN and reloading the web server) must be performed by the server
administrator following the procedure in docs/development/ops-runbook.md.

Closes #1543

ISSUES CLOSED: #1543
2026-04-02 23:59:37 +00:00

111 lines
5.6 KiB
Gherkin

Feature: TLS certificate health-check script
The ``scripts/check-tls-cert.py`` script inspects TLS certificates for
CleverAgents infrastructure hostnames and reports errors, warnings, and
certificate metadata. These scenarios exercise the script's core logic
using injected SSL contexts so no real network connections are made.
# Regression tests for issue #1543
# The TLS handshake failure on git.dev.cleveragents.com was caused by the
# hostname being absent from the certificate's Subject Alternative Names
# (SANs). The scenarios below verify that the check script correctly
# detects this condition and reports it as an error.
@tdd_issue @tdd_issue_1543
Scenario: Script detects missing SAN for git.dev.cleveragents.com
Given a TLS certificate for "git.cleverthis.com" with SANs "git.cleverthis.com,git.cleveragents.com"
And the certificate expires in 90 days
When the TLS check runs for hostname "git.dev.cleveragents.com"
Then the check result should be a failure
And the check error should mention "not found in certificate SANs"
@tdd_issue @tdd_issue_1543
Scenario: Script passes when hostname is present in SANs
Given a TLS certificate for "git.cleverthis.com" with SANs "git.cleverthis.com,git.dev.cleveragents.com"
And the certificate expires in 90 days
When the TLS check runs for hostname "git.dev.cleveragents.com"
Then the check result should be a success
@tdd_issue @tdd_issue_1543
Scenario: Script detects expired certificate
Given a TLS certificate for "git.cleverthis.com" with SANs "git.cleverthis.com,git.dev.cleveragents.com"
And the certificate expired 5 days ago
When the TLS check runs for hostname "git.dev.cleveragents.com"
Then the check result should be a failure
And the check error should mention "expired"
@tdd_issue @tdd_issue_1543
Scenario: Script warns when certificate expires within threshold
Given a TLS certificate for "git.cleverthis.com" with SANs "git.cleverthis.com,git.dev.cleveragents.com"
And the certificate expires in 15 days
When the TLS check runs for hostname "git.dev.cleveragents.com" with warn-days 30
Then the check result should be a success
And the check warning should mention "expires in"
@tdd_issue @tdd_issue_1543
Scenario: Script does not warn when certificate expires beyond threshold
Given a TLS certificate for "git.cleverthis.com" with SANs "git.cleverthis.com,git.dev.cleveragents.com"
And the certificate expires in 60 days
When the TLS check runs for hostname "git.dev.cleveragents.com" with warn-days 30
Then the check result should be a success
And the check has no warnings
@tdd_issue @tdd_issue_1543
Scenario: Script reports TLS handshake failure as an error
Given the TLS connection raises an SSLError "CERTIFICATE_VERIFY_FAILED"
When the TLS check runs for hostname "git.dev.cleveragents.com"
Then the check result should be a failure
And the check error should mention "TLS"
@tdd_issue @tdd_issue_1543
Scenario: Script reports connection timeout as an error
Given the TLS connection times out
When the TLS check runs for hostname "git.dev.cleveragents.com"
Then the check result should be a failure
And the check error should mention "timed out"
@tdd_issue @tdd_issue_1543
Scenario: Script reports connection refused as an error
Given the TLS connection is refused
When the TLS check runs for hostname "git.dev.cleveragents.com"
Then the check result should be a failure
And the check error should mention "Connection failed"
# ── Wildcard SAN matching ──────────────────────────────────────────────
@tdd_issue @tdd_issue_1543
Scenario: Script accepts wildcard SAN matching the hostname
Given a TLS certificate for "git.cleverthis.com" with SANs "*.cleverthis.com"
And the certificate expires in 90 days
When the TLS check runs for hostname "git.cleverthis.com"
Then the check result should be a success
@tdd_issue @tdd_issue_1543
Scenario: Script rejects wildcard SAN that does not match the hostname
Given a TLS certificate for "git.cleverthis.com" with SANs "*.cleveragents.com"
And the certificate expires in 90 days
When the TLS check runs for hostname "git.cleverthis.com"
Then the check result should be a failure
And the check error should mention "not found in certificate SANs"
# ── Helper function unit tests ─────────────────────────────────────────
@tdd_issue @tdd_issue_1543
Scenario: _hostname_matches_san returns True for exact match
When I check if hostname "git.dev.cleveragents.com" matches SANs "git.dev.cleveragents.com,git.cleveragents.com"
Then the SAN match result should be True
@tdd_issue @tdd_issue_1543
Scenario: _hostname_matches_san returns False when hostname is absent
When I check if hostname "git.dev.cleveragents.com" matches SANs "git.cleveragents.com,git.cleverthis.com"
Then the SAN match result should be False
@tdd_issue @tdd_issue_1543
Scenario: _hostname_matches_san handles wildcard SANs correctly
When I check if hostname "git.cleverthis.com" matches SANs "*.cleverthis.com"
Then the SAN match result should be True
@tdd_issue @tdd_issue_1543
Scenario: _hostname_matches_san rejects multi-level wildcard
When I check if hostname "a.b.cleverthis.com" matches SANs "*.cleverthis.com"
Then the SAN match result should be False