我们对于域名的管理,主要需要监控的状态包括域名过期时间、域名的证书过期时间,就简单介绍一下。

通常会在阿里云,或者腾讯云这类主流平台购买域名和证书,通常都会有相关的API接口可供查询,这里就不介绍相关SDK的使用了。

我们使用更加通用的方式来监控域名,获取其相关信息。

查询域名过期时间

就非常简单的示例,先安装依赖包

pip install whois

示例代码

import whois

domain = whois.query("xwl.io")
print(domain.__dict__)

print(domain.expiration_date)

我们可以打印出所有支持的字段信息,以下是相关输出,根据需求获取即可。

>>> print(domain.__dict__)
{
    'name': 'xwl.io', 
    'registrar': 'CloudFlare, Inc.', 
    'registrant_country': 'CN', 
    'creation_date': datetime.datetime(2021, 1, 30, 15, 54, 49), 
    'expiration_date': datetime.datetime(2023, 1, 30, 15, 54, 49), 
    'last_updated': datetime.datetime(2021, 5, 3, 21, 17, 4), 
    'status': 'clientTransferProhibited https://icann.org/epp#clientTransferProhibited', 
    'statuses': ['serverTransferProhibited https://icann.org/epp#serverTransferProhibited', 
    'clientTransferProhibited https://icann.org/epp#clientTransferProhibited'], 'dnssec': False, 
    'name_servers': {'ashley.ns.cloudflare.com\r', 'earl.ns.cloudflare.com\r'},           'registrant': 'DATA REDACTED'
}

查询域名证书过期时间

安装依赖包

pip install pyopenssl python-dateutil

以下是获取域名证书过期时间的示例脚本

import ssl
import time
import OpenSSL
from dateutil import parser

hostname = 'blog.xwl.io'
port = 443
cert = ssl.get_server_certificate((hostname, port)).encode()
cert_obj = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
cert_expire_time = parser.parse(cert.get_notAfter().decode("UTF-8")).strftime('%Y-%m-%d %H:%M:%S')

print(cert.has_expired())
print(cert_expire_time)

输出信息如下:

False
2022-05-07 23:59:59

由于我的网站挂在CloudFlare下,这个过程中也可能会出现一个错误
ssl.SSLError: [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:1122)

这是因为请求的时候没有传递主机名导致的,需要修改ssl模块如下

def get_server_certificate(addr, ssl_version=PROTOCOL_TLS, ca_certs=None):
    """Retrieve the certificate from the server at the specified address,
    and return it as a PEM-encoded string.
    If 'ca_certs' is specified, validate the server cert against it.
    If 'ssl_version' is specified, use it in the connection attempt."""

    host, port = addr
    if ca_certs is not None:
        cert_reqs = CERT_REQUIRED
    else:
        cert_reqs = CERT_NONE
    context = _create_stdlib_context(ssl_version,
                                     cert_reqs=cert_reqs,
                                     cafile=ca_certs)
    with  create_connection(addr) as sock:
        with context.wrap_socket(sock, server_hostname=host) as sslsock:
            dercert = sslsock.getpeercert(True)
    return DER_cert_to_PEM_cert(dercert)

主要就是为这一行with context.wrap_socket(sock, server_hostname=host) as sslsock:,加上server_hostname字段。