Skip to content

Commit ab0dd50

Browse files
committed
New feature: Checking HTTP codes
1 parent 98cddfa commit ab0dd50

File tree

6 files changed

+52
-8
lines changed

6 files changed

+52
-8
lines changed

.github/workflows/base.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,4 +78,5 @@ jobs:
7878
python3 ssl-check-to-slack.py
7979
env:
8080
HOOK_URL: "https://hooks.slack.com/services/XXXXXXX/XXXXXXX/XXXXXXXXXXXX"
81-
HOSTNAMES: "fivexl.io"
81+
HOSTNAMES: "fivexl.io"
82+
HEALTH_CHECK_MATCHER: "200-399"

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Simple SSL check and expiring certificates reminder with additional DNS check an
77
```hcl
88
module "ssl_checker" {
99
source = "fivexl/ssl-checker/aws"
10-
version = "1.0.0"
10+
version = "1.0.1"
1111
hostnames = ["fivexl.io", "google.com"]
1212
slack_hook_url = "https://hooks.slack.com/services/XXXXXXX/XXXXXXX/XXXXXXXXXXXX"
1313
}
@@ -32,6 +32,7 @@ module "ssl_checker" {
3232
| cloudwatch_logs_retention_in_days | Specifies the number of days you want to retain log events in the specified log group. Possible values are: 1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1827, and 3653. | `number` | `14` | no |
3333
| function_name | Lambda function name. | `string` | `"ssl-checker"` | no |
3434
| hostnames | The list of DNS names that should be monitored. e.g.: [\"example.com\"]. | `list(string)` | | yes |
35+
| health_check_matcher | The response HTTP codes to use when checking for a healthy responses from a hostnames. e.g.: \"200,201,202-399\". | `string` | `"200-399"` | no |
3536
| scan_commands | List of scan commands types witch will run against hostnames. Any type supported by [SSLyze](https://nabla-c0d3.github.io/sslyze/documentation/available-scan-commands.html). | `list(string)` | `["certificate_info", "robot", "tls_compression", "tls_fallback_scsv", "heartbleed","http_headers", "openssl_ccs_injection", "session_renegotiation", "tls_1_1_cipher_suites","tls_1_2_cipher_suites", "tls_1_3_cipher_suites"]` | no |
3637
| schedule_expression | The scheduling expression. How often check hostnames. For example, `cron(0/5 * * * ? *)` or `rate(5 minutes)`. | `string` | `"cron(0/5 * * * ? *)"` | no |
3738
| slack_hook_url | Slack incoming webhook URL. | `string` | | yes |
@@ -50,6 +51,7 @@ Configuration is done via env variables
5051

5152
* `HOOK_URL` - Slack web hook URL where to send events. This is a mandatory parameter.
5253
* `HOSTNAMES` - Comma separated string with domain names. This is a mandatory parameter.
54+
* `HEALTH_CHECK_MATCHER` - The response HTTP codes to use when checking for a healthy responses from a hostnames. You can specify multiple values (for example, "200,202" for HTTP(s)) or a range of values (for example, "200-299" or "0-99"). Default - `'200-399'`
5355
* `CERTIFICATE_EXPIRATION_NOTICE_DAYS` - How many days before the expiration date of the certificate to send reminders. Default - `'7'`
5456
* `SCAN_COMMANDS` - Comma separated string with scan commands types witch will run against hostnames. Any type supported by SSLyze.
5557

docker-compose.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ services:
77
environment:
88
HOOK_URL: "https://hooks.slack.com/services/XXXXXXXXXXXXXXXXXXXXXXXXXX"
99
HOSTNAMES: "fivexl.io"
10+
HEALTH_CHECK_MATCHER: "200-399"
1011
CERTIFICATE_EXPIRATION_NOTICE_DAYS: "7"

main.tf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ module "lambda" {
3232
environment_variables = {
3333
HOOK_URL = var.slack_hook_url
3434
HOSTNAMES = join(",", var.hostnames)
35+
HEALTH_CHECK_MATCHER = var.health_check_matcher
3536
CERTIFICATE_EXPIRATION_NOTICE_DAYS = var.certificate_expiration_notice_days
3637
SCAN_COMMANDS = join(",", var.scan_commands)
3738
}

ssl-check-to-slack.py

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,26 @@ def read_env_variable_or_die(env_var_name):
2626
return value
2727

2828

29+
def get_response_status(hostname):
30+
connection = http.client.HTTPSConnection(hostname)
31+
connection.request("GET", "/")
32+
response = connection.getresponse()
33+
return response.status
34+
35+
36+
def split_matcher(matcher):
37+
result = []
38+
comma_list = matcher.split(',')
39+
for item in comma_list:
40+
dash_list = item.split('-')
41+
if len(dash_list) == 1:
42+
result.append(int(dash_list[0]))
43+
else:
44+
for element in list(range(int(dash_list[0]), int(dash_list[1]) + 1)):
45+
result.append(element)
46+
return set(result)
47+
48+
2949
# Slack web hook example
3050
# https://hooks.slack.com/services/XXXXXXX/XXXXXXX/XXXXXXXXXXXX
3151
def post_slack_message(hook_url, message):
@@ -72,6 +92,7 @@ def main(event, context):
7292
logger.info('Reading configuration...')
7393
slack_web_hook_url = read_env_variable_or_die('HOOK_URL')
7494
hostnames = read_env_variable_or_die('HOSTNAMES').split(',')
95+
health_check_matcher = split_matcher(str(os.environ.get('HEALTH_CHECK_MATCHER', '200-399,201')))
7596
certificate_expiration_notice_days = int(os.environ.get('CERTIFICATE_EXPIRATION_NOTICE_DAYS', '7'))
7697
# https://nabla-c0d3.github.io/sslyze/documentation/available-scan-commands.html
7798
scan_commands = set(os.environ.get('SCAN_COMMANDS',
@@ -92,12 +113,17 @@ def main(event, context):
92113
try:
93114
logger.debug(f'Connect: {hostname} - Testing...')
94115
server_info = ServerConnectivityTester().perform(server_location)
116+
response_status = get_response_status(hostname)
117+
if response_status not in health_check_matcher:
118+
raise ConnectionToServerFailed(server_info.server_location, server_info.network_configuration,
119+
error_message=f'HTTP Error. Status code: {response_status}')
95120
servers_to_scan.append(server_info)
96121
logger.debug(f'Connect: {hostname} - OK')
97122
except ConnectionToServerFailed as e:
98123
logger.error(f'Connect: {hostname} - ERROR: {e.error_message}')
99124
message = f'URL is not available! Connect error: {e.error_message}'
100-
post_slack_message(slack_web_hook_url, format_ssl_check_to_slack_message(f'https://{hostname}', message))
125+
post_slack_message(slack_web_hook_url,
126+
format_ssl_check_to_slack_message(f'https://{hostname}', message))
101127
except Exception as e:
102128
logger.error(f'Connect: {hostname} - ERROR: {e}')
103129
post_slack_message(slack_web_hook_url, format_error_to_slack_message(str(e)))
@@ -139,29 +165,36 @@ def main(event, context):
139165
logger.error(f'TLS: {server_scan_result_hostname} - ERROR: Not valid before: {not_valid_before}. '
140166
f'Now is: {date_current}')
141167
message = f'SSL certificate not valid before: {not_valid_before}. Now is: {date_current}'
142-
post_slack_message(slack_web_hook_url, format_ssl_check_to_slack_message(f'https://{server_scan_result_hostname}', message))
168+
post_slack_message(slack_web_hook_url,
169+
format_ssl_check_to_slack_message(f'https://{server_scan_result_hostname}',
170+
message))
143171
delta = not_valid_after - date_current
144172
if delta.days <= certificate_expiration_notice_days:
145173
logger.error(f'TLS: {server_scan_result_hostname} - ERROR: Expires in less than {delta.days} days')
146174
message = f'SSL certificate expires in less than: {delta.days} days.'
147-
post_slack_message(slack_web_hook_url, format_ssl_check_to_slack_message(f'https://{server_scan_result_hostname}', message))
175+
post_slack_message(slack_web_hook_url,
176+
format_ssl_check_to_slack_message(f'https://{server_scan_result_hostname}',
177+
message))
148178
else:
149179
logger.info(f'TLS: {server_scan_result_hostname} - OK: Valid for next {delta.days} days')
150180
if not any(subject_matches_hostname):
151181
logger.error(f'TLS: {server_scan_result_hostname} - ERROR: No subject matches')
152182
message = 'SSL certificate no subject matches.'
153-
post_slack_message(slack_web_hook_url, format_ssl_check_to_slack_message(f'https://{server_scan_result_hostname}', message))
183+
post_slack_message(slack_web_hook_url,
184+
format_ssl_check_to_slack_message(f'https://{server_scan_result_hostname}', message))
154185
if not all(chain_has_valid_order):
155186
logger.error(f'TLS: {server_scan_result_hostname} - ERROR: Chain has no valid order')
156187
message = 'SSL certificate chain has no valid order.'
157-
post_slack_message(slack_web_hook_url, format_ssl_check_to_slack_message(f'https://{server_scan_result_hostname}', message))
188+
post_slack_message(slack_web_hook_url,
189+
format_ssl_check_to_slack_message(f'https://{server_scan_result_hostname}', message))
158190
except KeyError:
159191
pass
160192
# Scan commands that were run with errors
161193
for scan_command, error in server_scan_result.scan_commands_errors.items():
162194
logger.error(f'{scan_command}: {server_scan_result_hostname} - ERROR: {error.exception_trace}')
163195
message = f'{scan_command} failed with error: {error.exception_trace}'
164-
post_slack_message(slack_web_hook_url, format_ssl_check_to_slack_message(f'https://{server_scan_result_hostname}', message))
196+
post_slack_message(slack_web_hook_url,
197+
format_ssl_check_to_slack_message(f'https://{server_scan_result_hostname}', message))
165198

166199

167200
if __name__ == '__main__':

variables.tf

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ variable "hostnames" {
2121
type = list(string)
2222
}
2323

24+
variable "health_check_matcher" {
25+
description = "The response HTTP codes to use when checking for a healthy responses from a hostnames. e.g.: \"200,201,202-399\"."
26+
type = string
27+
default = "200-399"
28+
}
29+
2430
# https://nabla-c0d3.github.io/sslyze/documentation/available-scan-commands.html
2531
variable "scan_commands" {
2632
description = "List of scan commands types witch will run against hostnames. Any type supported by SSLyze."

0 commit comments

Comments
 (0)