#!/usr/bin/env python3 """Test: verify Caddy obtains a valid TLS certificate for a subdomain handle.""" import argparse import os import re import sys import time sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..')) from utils.tests.helpers import run, resolve_domain def run_in_container(domain, cmd): """Run a command inside the app container via abra.""" result = run( f'''script -qefc "abra app run {domain} app --no-tty -- sh -c '{cmd}' 2>&1" /dev/null''', check=False, timeout=120, ) return result.stdout def delete_account(domain, did, admin_pw_flag, pds_flag): print(f" Deleting {did} ...") run_in_container(domain, f"goat pds admin account delete {did} {admin_pw_flag} {pds_flag} 2>&1") def main(): parser = argparse.ArgumentParser() parser.add_argument('--domain', default=os.environ.get('TEST_DOMAIN')) args = parser.parse_args() domain = args.domain or resolve_domain('bluesky-pds') suffix = str(int(time.time())) test_handle = f"tlstest{suffix}.{domain}" test_email = f"tlstest{suffix}@{domain}" test_password = "testpass-subdomain-tls-check" pds_host = "http://localhost:3000" admin_pw_flag = "--admin-password \\$(cat /run/secrets/pds_admin_password)" pds_flag = f"--pds-host {pds_host}" max_attempts = 12 retry_interval = 10 # Step 1: Clean up old tlstest accounts print("Step 1: Cleaning up old tlstest accounts if present ...") # List all accounts, then check each one for tlstest handles account_list = run_in_container( domain, f"goat pds admin account list {admin_pw_flag} {pds_flag} 2>&1", ) deleted = [] for did in re.findall(r'did:plc:\w+', account_list): info = run_in_container( domain, f"goat pds admin account info {did} {admin_pw_flag} {pds_flag} 2>&1", ) if re.search(r'"handle": "tlstest\d+\.', info): print(f" Found old tlstest account ({did}), deleting ...") run_in_container( domain, f"goat pds admin account delete {did} {admin_pw_flag} {pds_flag} 2>&1", ) deleted.append(did) if not deleted: print(" No old accounts found.") # Step 2: Create the test account print(f"Step 2: Creating test account ({test_handle}) ...") create_output = run_in_container( domain, f"goat pds admin account create {admin_pw_flag} {pds_flag} --handle {test_handle} --email {test_email} --password {test_password} 2>&1", ) test_did_match = re.search(r'did:plc:\w+', create_output) if not test_did_match: print("FAIL: Could not create test account") print(f"Output: {create_output}") sys.exit(1) test_did = test_did_match.group() print(f" Created account {test_did}") # Step 3: Wait for Caddy to obtain a valid TLS cert print(f"Step 3: Waiting for valid TLS cert on https://{test_handle} ...") cert_ok = False for attempt in range(1, max_attempts + 1): print(f" Attempt {attempt}/{max_attempts} ...") result = run( f'curl -sf --max-time 15 "https://{test_handle}/xrpc/_health" -o /dev/null', check=False, timeout=20, ) if result.returncode == 0: cert_ok = True break if attempt < max_attempts: time.sleep(retry_interval) if not cert_ok: print(f"FAIL: Caddy did not obtain a valid TLS cert for {test_handle} after {max_attempts * retry_interval}s") delete_account(domain, test_did, admin_pw_flag, pds_flag) sys.exit(1) print(f"PASS: Valid TLS cert obtained for {test_handle}") # Step 4: Verify cert details print("Step 4: Checking certificate subject ...") result = run( f'echo | openssl s_client -servername {test_handle} -connect {test_handle}:443 2>/dev/null | openssl x509 -noout -subject 2>/dev/null', check=False, timeout=15, ) cert_subject = result.stdout.strip() print(f" {cert_subject}") if test_handle in cert_subject: print(f"PASS: Certificate subject matches {test_handle}") else: # Check SANs result = run( f'echo | openssl s_client -servername {test_handle} -connect {test_handle}:443 2>/dev/null | openssl x509 -noout -text 2>/dev/null | grep -A1 "Subject Alternative Name"', check=False, timeout=15, ) cert_san = result.stdout.strip() print(f" SANs: {cert_san}") if test_handle in cert_san: print(f"PASS: Certificate SAN matches {test_handle}") else: print(f"FAIL: Certificate does not match {test_handle}") delete_account(domain, test_did, admin_pw_flag, pds_flag) sys.exit(1) # Step 5: Cleanup delete_account(domain, test_did, admin_pw_flag, pds_flag) print("PASS: Subdomain TLS test passed") if __name__ == '__main__': main()