Skip to content

Commit

Permalink
Update to v1.0.2
Browse files Browse the repository at this point in the history
  • Loading branch information
HubTou authored Feb 21, 2024
1 parent 309fecb commit 05f2b55
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 23 deletions.
19 changes: 12 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,20 @@ file [...]
The **certwatch** utility monitors [X509 certificates](https://en.wikipedia.org/wiki/X.509) expiration dates
by processing one or more data files containing lists of hostnames with optional port numbers.

It's mainly used to check the expiration date of [HTTPS](https://en.wikipedia.org/wiki/HTTPS) certificates (which is the default target when the port number is not indicated),
but the tool is protocol-agnostic and can "talk" to any SNI-aware ([Server Name Information](https://en.wikipedia.org/wiki/Server_Name_Indication)) [SSL/TLS](https://en.wikipedia.org/wiki/Transport_Layer_Security) server (smtps, imaps, ldaps, etc.)
It's mainly used to check the expiration date of [HTTPS](https://en.wikipedia.org/wiki/HTTPS) certificates
(which is the default target when the port number is not indicated),
but the tool is protocol-agnostic and can "talk" to any SNI-aware ([Server Name Information](https://en.wikipedia.org/wiki/Server_Name_Indication))
[SSL/TLS](https://en.wikipedia.org/wiki/Transport_Layer_Security) server (smtps, imaps, ldaps, etc.)
without making too much assumptions on the correctness of servers certificates.

The certificates can be saved to a specified directory with the *--savedir|-s* option for further analysis with other tools (such as [OpenSSL](https://www.openssl.org/)).

As it's intended to bulk process a lot of certificates, a progress bar is displayed (can be removed with the *--noprogress|-b* option)
and the time allowed to get a certificate is limited to a 10 seconds timeout (can be specified otherwise with the *--timeout|-t* option).
and the time allowed to get a certificate is limited to a 10 seconds timeout
(can be specified otherwise with the *--timeout|-t* option, but is not supported on Windows systems).

In order to avoid doing a [Denial of Service attack](https://en.wikipedia.org/wiki/Denial-of-service_attack) on servers hosting many certificates, a 1 second delay is waited between each certificate request
(can be specified otherwise with the *--delay|-d* option).
In order to avoid doing a [Denial of Service attack](https://en.wikipedia.org/wiki/Denial-of-service_attack) on servers hosting many certificates,
a 1 second delay is waited between each certificate request (can be specified otherwise with the *--delay|-d* option).

The tool results are presented as text tables.

Expand Down Expand Up @@ -82,7 +85,9 @@ Options | Use
The CERTWATCH_DEBUG environment variable can be set to any value to enable debug mode.

## FILES
[/usr/local/share/certwatch/tests.txt](https://github.com/HubTou/certwatch/blob/main/data/tests.txt) - config file example using the [badssl.com](https://badssl.com) Web site for testing live bogus X509 certificates
[/usr/local/share/certwatch/tests.txt](https://github.com/HubTou/certwatch/blob/main/data/tests.txt) - config file example using the [badssl.com](https://badssl.com) Web site for testing live bogus X509 certificates,
with [text](https://github.com/HubTou/certwatch/blob/main/data/tests_output.txt)
and [Excel](https://github.com/HubTou/certwatch/blob/main/data/tests_output.xlsx) output.

The structure of configuration files is as follows:
* Everything after a '#' character is a comment
Expand Down Expand Up @@ -118,7 +123,7 @@ The **certwatch** utility is not a standard UNIX command.
It tries to follow the [PEP 8](https://www.python.org/dev/peps/pep-0008/) style guide for [Python](https://www.python.org/) code.

## PORTABILITY
To be tested under Windows.
Tested OK under Windows.

## HISTORY
This implementation was made for the [PNU project](https://github.com/HubTou/PNU).
Expand Down
62 changes: 62 additions & 0 deletions data/tests_output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@

Certificates expiration dates: (showing all 11)
┌────────────────────────────────────┬────────────────┬────────────────────┬───────────────────────┬───────────────────┬───────────────────────────┐
│ hostname:port │ IP address │ common name │ alt names │ issuer org name │ not valid after │
├────────────────────────────────────┼────────────────┼────────────────────┼───────────────────────┼───────────────────┼───────────────────────────┤
│ expired.badssl.com:443 │ 104.154.89.105 │ *.badssl.com │ *.badssl.com │ COMODO CA Limited │ 2015-04-12 23:59:59+00:00 │
│ │ │ │ badssl.com │ │ │
│ │ │ │ │ │ │
│ no-subject.badssl.com:443 │ 104.154.89.105 │ │ no-subject.badssl.com │ COMODO CA Limited │ 2020-06-16 23:59:59+00:00 │
│ │ │ │ │ │ │
│ incomplete-chain.badssl.com:443 │ 104.154.89.105 │ *.badssl.com │ *.badssl.com │ DigiCert Inc │ 2022-05-17 12:00:00+00:00 │
│ │ │ │ badssl.com │ │ │
│ │ │ │ │ │ │
│ revoked.badssl.com:443 │ 104.154.89.105 │ revoked.badssl.com │ revoked.badssl.com │ DigiCert Inc │ 2022-10-27 23:59:59+00:00 │
│ │ │ │ │ │ │
│ client-cert-missing.badssl.com:443 │ 104.154.89.105 │ *.badssl.com │ *.badssl.com │ Let's Encrypt │ 2024-02-27 21:30:03+00:00 │
│ │ │ │ badssl.com │ │ │
│ │ │ │ │ │ │
│ pinning-test.badssl.com:443 │ 104.154.89.105 │ *.badssl.com │ *.badssl.com │ Let's Encrypt │ 2024-02-27 21:30:03+00:00 │
│ │ │ │ badssl.com │ │ │
│ │ │ │ │ │ │
│ wrong.host.badssl.com:443 │ 104.154.89.105 │ *.badssl.com │ *.badssl.com │ Let's Encrypt │ 2024-02-27 21:30:03+00:00 │
│ │ │ │ badssl.com │ │ │
│ │ │ │ │ │ │
│ github.com:443 │ 140.82.121.4 │ github.com │ github.com │ DigiCert Inc │ 2024-03-14 23:59:59+00:00 │
│ │ │ │ www.github.com │ │ │
│ │ │ │ │ │ │
│ no-sct.badssl.com:443 │ 104.154.89.105 │ no-sct.badssl.com │ no-sct.badssl.com │ DigiCert Inc │ 2024-11-26 23:59:59+00:00 │
│ │ │ │ www.no-sct.badssl.com │ │ │
│ │ │ │ │ │ │
│ self-signed.badssl.com:443 │ 104.154.89.105 │ *.badssl.com │ *.badssl.com │ BadSSL │ 2025-11-28 22:34:04+00:00 │
│ │ │ │ badssl.com │ │ │
│ │ │ │ │ │ │
│ untrusted-root.badssl.com:443 │ 104.154.89.105 │ *.badssl.com │ *.badssl.com │ BadSSL │ 2025-11-28 22:34:04+00:00 │
│ │ │ │ badssl.com │ │ │
│ │ │ │ │ │ │
└────────────────────────────────────┴────────────────┴────────────────────┴───────────────────────┴───────────────────┴───────────────────────────┘

Server errors: (showing all 3)
┌───────────────────────────┬────────────────┬──────────────┬──────┬───────────────┬───────────────────────────────┬───────────────────────┐
│ hostname │ IP address │ is pingable? │ port │ is listening? │ error type │ is listening on http? │
├───────────────────────────┼────────────────┼──────────────┼──────┼───────────────┼───────────────────────────────┼───────────────────────┤
│ no-common-name.badssl.com │ 104.154.89.105 │ NO │ 443 │ YES │ Connection failed │ YES │
│ nonexistent.com │ │ N/A │ 443 │ N/A │ Name does not resolve │ N/A │
│ null.badssl.com │ 104.154.89.105 │ NO │ 443 │ NO │ sslv3 alert handshake failure │ YES │
└───────────────────────────┴────────────────┴──────────────┴──────┴───────────────┴───────────────────────────────┴───────────────────────┘

Common names unmentioned in your input files:
┌──────────────┐
│ common name │
├──────────────┤
│ *.badssl.com │
└──────────────┘

Alt names unmentioned in your input files:
┌───────────────────────┐
│ alt name │
├───────────────────────┤
│ www.github.com │
│ badssl.com │
│ www.no-sct.badssl.com │
└───────────────────────┘
Binary file added data/tests_output.xlsx
Binary file not shown.
8 changes: 4 additions & 4 deletions man/certwatch.1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.Dd February 20, 2024
.Dd February 21, 2024
.Dt CERTWATCH 1
.Os
.Sh NAME
Expand Down Expand Up @@ -39,7 +39,7 @@ As it's intended to bulk process a lot of certificates, a progress bar is displa
.Op Fl \-noprogress|\-b
option) and the time allowed to get a certificate is limited to a 10 seconds timeout (can be specified otherwise with the
.Op Fl \-timeout|\-t
option).
option, but is not supported on Windows systems).
.Pp
In order to avoid doing a Denial of Service attack on servers hosting many certificates, a 1 second delay is waited between each certificate request
(can be specified otherwise with the
Expand Down Expand Up @@ -115,7 +115,7 @@ The
environment variable can be set to any value to enable debug mode.
.Sh FILES
.Pa /usr/local/share/certwatch/tests.txt
\- config file example using the badssl.com Web site for testing live bogus X509 certificates
\- config file example using the badssl.com Web site for testing live bogus X509 certificates.
.Pp
The structure of configuration files is as follows:
.Bl -bullet
Expand Down Expand Up @@ -167,7 +167,7 @@ utility is not a standard UNIX command.
.Pp
It tries to follow the PEP 8 style guide for Python code.
.Sh PORTABILITY
To be tested under Windows.
Tested OK under Windows.
.Sh HISTORY
This implementation was made for the
.Lk https://github.com/HubTou/PNU PNU project
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name = pnu-certwatch
description = watch X509 certificates expiration dates
long_description = file: README.md
long_description_content_type = text/markdown
version = 1.0.0
version = 1.0.2
license = BSD 3-Clause License
license_files = License
author = Hubert Tournier
Expand Down
29 changes: 19 additions & 10 deletions src/certwatch/library.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,16 +279,18 @@ def _is_pingable(hostname):
def _is_listening(hostname, hostport, timeout=10):
""" Attempts a connection on the given port and reports if it's open or not """
sock = socket.socket()
signal.signal(signal.SIGALRM, _timeout_signal_handler)
signal.alarm(timeout)
if platform.system().lower() != "windows":
signal.signal(signal.SIGALRM, _timeout_signal_handler)
signal.alarm(timeout)
try:
sock.connect((hostname, hostport))
sock.close()
listening = True
except socket.error:
listening = False
finally:
signal.alarm(0)
if platform.system().lower() != "windows":
signal.alarm(0)

return listening

Expand Down Expand Up @@ -333,7 +335,8 @@ def get_certs(input_data, progress_bar=True, delay=1, timeout=10, save_cert_dir=
""" Return dictionaries of certificates fetched and errors encountered """
certs = {}
errors = {}
signal.signal(signal.SIGALRM, _timeout_signal_handler)
if platform.system().lower() != "windows":
signal.signal(signal.SIGALRM, _timeout_signal_handler)
if progress_bar:
for i in tqdm.tqdm(range(len(input_data))):
hostname = input_data[i][0]
Expand All @@ -347,14 +350,17 @@ def get_certs(input_data, progress_bar=True, delay=1, timeout=10, save_cert_dir=
error_message = str(error)

if ip_address:
signal.alarm(timeout)
if platform.system().lower() != "windows":
signal.alarm(timeout)
try:
cert = _get_cert(hostname, hostport, save_cert_dir)
except (NameError, TimeoutError, ConnectionError) as error:
signal.alarm(0)
if platform.system().lower() != "windows":
signal.alarm(0)
errors[hostname + ":" + str(hostport)] = _analyze_error(hostname, hostport, ip_address, str(error), type(error).__name__)
continue
signal.alarm(0)
if platform.system().lower() != "windows":
signal.alarm(0)

cert["IP"] = ip_address
certs[hostname + ":" + str(hostport)] = cert
Expand All @@ -376,14 +382,17 @@ def get_certs(input_data, progress_bar=True, delay=1, timeout=10, save_cert_dir=
error_message = str(error)

if ip_address:
signal.alarm(timeout)
if platform.system().lower() != "windows":
signal.alarm(timeout)
try:
cert = _get_cert(hostname, hostport, save_cert_dir)
except (NameError, TimeoutError, ConnectionError) as error:
signal.alarm(0)
if platform.system().lower() != "windows":
signal.alarm(0)
errors[hostname + ":" + str(hostport)] = _analyze_error(hostname, hostport, ip_address, str(error), type(error).__name__)
continue
signal.alarm(0)
if platform.system().lower() != "windows":
signal.alarm(0)

cert["IP"] = ip_address
certs[hostname + ":" + str(hostport)] = cert
Expand Down
5 changes: 4 additions & 1 deletion src/certwatch/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
get_new_names, print_new_names, update_excel

# Version string used by the what(1) and ident(1) commands:
ID = "@(#) $Id: certwatch - watch X509 certificates expiration dates v1.0.0 (February 20, 2024) by Hubert Tournier $"
ID = "@(#) $Id: certwatch - watch X509 certificates expiration dates v1.0.2 (February 21, 2024) by Hubert Tournier $"

# Default parameters. Can be overcome by environment variables, then command line options
parameters = {
Expand Down Expand Up @@ -137,6 +137,9 @@ def _process_command_line():
sys.exit(1)

elif option in ("--excel", "-e"):
if not argument.endswith(".xlsx"):
logging.critical("--excel|-e parameter must use a .xlsx extension")
sys.exit(1)
parameters["Excel filename"] = argument

elif option in ("--filter", "-f"):
Expand Down

0 comments on commit 05f2b55

Please sign in to comment.