**Approach**1. For each host:port, open a TCP socket with a timeout, wrap with ssl to get the peer certificate.2. Parse certificate's Not After field, compare against now + 30 days.3. Handle socket/SSL/timeouts/errors gracefully and report.python
#!/usr/bin/env python3
import ssl, socket, sys
from datetime import datetime, timedelta
# Read host:port lines from a file provided as first arg, or stdin
def read_targets(path=None):
if path:
with open(path) as f:
lines = f.readlines()
else:
lines = sys.stdin.read().splitlines()
for raw in lines:
line = raw.strip()
if not line or line.startswith('#'):
continue
if ':' in line:
host, port = line.rsplit(':', 1)
yield host.strip(), int(port.strip())
else:
yield line.strip(), 443
def check_cert_expiry(host, port, timeout=5):
context = ssl.create_default_context()
# don't verify hostname or CA; we only want the cert fields
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE
with socket.create_connection((host, port), timeout=timeout) as sock:
with context.wrap_socket(sock, server_hostname=host) as ssock:
cert = ssock.getpeercert()
not_after = cert.get('notAfter')
if not not_after:
raise ValueError('no notAfter in cert')
# Example format: 'Jun 30 12:00:00 2026 GMT'
expires = datetime.strptime(not_after, '%b %d %H:%M:%S %Y %Z')
return expires
def main():
path = sys.argv[1] if len(sys.argv) > 1 else None
soon = datetime.utcnow() + timedelta(days=30)
problem_hosts = []
for host, port in read_targets(path):
try:
expires = check_cert_expiry(host, port)
if expires < soon:
days_left = (expires - datetime.utcnow()).days
problem_hosts.append(f'{host}:{port} expires in {days_left} day(s) on {expires} UTC')
except Exception as e:
print(f'ERROR {host}:{port} -> {e}')
if problem_hosts:
print('Certificates expiring within 30 days:')
for line in problem_hosts:
print(' -', line)
else:
print('No certificates expiring within 30 days.')
if __name__ == '__main__':
main()
Notes, edge cases and reasoning- Uses a short socket timeout to avoid long waits; errors are caught and printed.- Parses common OpenSSL notAfter format; some servers may return different timezone tags — adjust parsing if needed.- For production, enable certificate validation and parallelize checks (threads/async) for large lists.