Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Remy committed Aug 6, 2015
0 parents commit 786395f
Show file tree
Hide file tree
Showing 29 changed files with 1,671 additions and 0 deletions.
54 changes: 54 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Certificate Expiry Monitor

## About

Certificate Expiry Monitor is an open source monitoring tool for certificates. It monitors websites and emails you when the certificates are about to expire.

See the example site: https://certificatemonitor.org/

## Requirements

- PHP 5.6+
- OpenSSL
- PHP must allow remote fopen.

## Installation

Unpack, change some variables, setup a cronjob and go!

First get the code and unpack it to your webroot:

cd /var/www/html/
git clone https://github.com/RaymiiOrg/certificate-expiry-monitor.git

Create the database files, outside of your webroot. If you create these inside your webroot, everybody can read them.

touch /var/www/certificate-expiry-monitor-db/pre_checks.json
touch /var/www/certificate-expiry-monitor-db/checks.json
touch /var/www/certificate-expiry-monitor-db/deleted_checks.json
chown -R $wwwuser /var/www/certificate-expiry-monitor-db/*.json

These files are used by the tool as database for checks.


Change the location of these files in `variables.php`:


// set this to a location outside of your webroot so that it cannot be accessed via the internets.

$pre_check_file = '/var/ww/html/certificate-expiry-monitor/pre_checks.json';
$check_file = '/var/ww/html/certificate-expiry-monitor/checks.json';
$deleted_check_file = '/var/ww/html/certificate-expiry-monitor/deleted_checks.json';

Also change the `$current_domain` variable, it is used in all the emails.

$current_domain = "certificatemonitor.org";

Set up the cronjob to run once a day:

# /etc/cron.d/certificate-exipry-monitor
1 1 * * * $wwwuser /path/to/php /var/ww/html/certificate-expiry-monitor/cron.php >> /var/log/certificate-expiry-monitor.log 2>&1


The default timeout for checks is 2 seconds. If this is to fast for your internal services, this can be raised in the `variables.php` file.

88 changes: 88 additions & 0 deletions add.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<?php
// Copyright (C) 2015 Remy van Elst

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.

// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

error_reporting(E_ALL & ~E_NOTICE);
foreach (glob("functions/*.php") as $filename) {
require($filename);
}

require('inc/header.php');

echo "<div class='content'><section id='result'>";

if ( isset($_POST['email']) && !empty($_POST['email']) && isset($_POST['domains']) && !empty($_POST['domains']) ) {

$errors = array();
if (validate_email($_POST['email'])) {
$email = htmlspecialchars($_POST['email']);
} else {
$errors[] = "Invalid email address.";
}

$domains = validate_domains($_POST['domains']);
if ( count($domains['errors']) >= 1 ) {
foreach ($domains['errors'] as $key => $value) {
$errors[] = $value;
}
}

if (is_array($errors) && count($errors) != 0) {
$errors = array_unique($errors);
foreach ($errors as $key => $value) {
echo "<div class='alert alert-danger' role='alert'>";
echo htmlspecialchars($value);
echo "</div>";
}
echo "Please return and try again.<br>";
} elseif ( is_array($errors) && count($errors) == 0 && is_array($domains['domains']) && count($domains['domains']) != 0 && count($domains['domains']) < 21) {
echo "<div class='alert alert-info' role='alert'>";
echo "Email: " . htmlspecialchars($email) . ".<br>";
echo "</div>";
foreach ($domains['domains'] as $key => $value) {
$add_domain = add_domain_to_pre_check($value, $email, $_SERVER['REMOTE_ADDR']);
if (is_array($add_domain["errors"]) && count($add_domain["errors"]) != 0) {
$errors = array_unique($add_domain["errors"]);
foreach ($add_domain["errors"] as $key => $err_value) {
echo "<div class='alert alert-danger' role='alert'>";
echo htmlspecialchars($err_value);
echo "</div>";
}
} else {
echo "<div class='alert alert-success' role='alert'>";
echo "Confirmation email sent. Please confirm your subscription email to complete the process.<br>";
echo "</div>";
}
}
} else {
echo "<div class='alert alert-danger' role='alert'>";
echo "Too many domains.<br>";
echo "Please return and try again.<br>";
echo "</div>";
}
} else {

echo "<div class='alert alert-danger' role='alert'>";;
echo "Error. Domain(s) and email address are required.<br>";
echo "Please return and try again.<br>";
echo "</div>";
}


require('inc/faq.php');

require('inc/footer.php');

?>
58 changes: 58 additions & 0 deletions confirm.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php
// Copyright (C) 2015 Remy van Elst

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.

// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

error_reporting(E_ALL & ~E_NOTICE);
foreach (glob("functions/*.php") as $filename) {
require($filename);
}

require('inc/header.php');

if ( isset($_GET['id']) && !empty($_GET['id']) ) {
$id = htmlspecialchars($_GET['id']);
$uuid_pattern = "/([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})/";
if (preg_match($uuid_pattern, $id)) {
$add_domain = add_domain_check($id, $_SERVER['REMOTE_ADDR']);
if (is_array($add_domain["errors"]) && count($add_domain["errors"]) != 0) {
$errors = array_unique($add_domain["errors"]);
foreach ($add_domain["errors"] as $key => $err_value) {
echo "<div class='alert alert-danger' role='alert'>";
echo htmlspecialchars($err_value);
echo "</div>";
}
} else {
echo "<div class='alert alert-success' role='alert'>";
echo "Check added. You will now receive notifications on certificate expiration events as described in the FAQ.<br>";
echo "</div>";
}
} else {
echo "<div class='alert alert-danger' role='alert'>";;
echo "Error. ID is invalid.<br>";
echo "Please return and try again.<br>";
echo "</div>";
}
} else {
echo "<div class='alert alert-danger' role='alert'>";;
echo "Error. ID is required.<br>";
echo "Please return and try again.<br>";
echo "</div>";
}

require('inc/faq.php');

require('inc/footer.php');

?>
164 changes: 164 additions & 0 deletions cron.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
<?php
// Copyright (C) 2015 Remy van Elst

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.

// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

error_reporting(E_ALL & ~E_NOTICE);
$result = array();
if (php_sapi_name() == "cli") {
foreach (glob( __DIR__ . "/functions/*.php") as $filename) {
require($filename);
}

$removal_queue = array();
$tmp_check_file = $check_file . ".tmp";
if (!copy($check_file, $tmp_check_file)) {
echo "Failed to copy $check_file to $tmp_check_file.\n";
break;
}

$file = file_get_contents($tmp_check_file);
if ($file === FALSE) {
$result['errors'][] = "Can't open database.";
}
$json_a = json_decode($file, true);
if ($json_a === null && json_last_error() !== JSON_ERROR_NONE) {
$result['errors'][] = "Can't read database: " . htmlspecialchars(json_last_error());

}

if (count($json_a) == 0) {
echo "Empty checklist.\n";
exit;
}

foreach ($json_a as $key => $value) {
$domain = $value['domain'];
$email = $value['email'];
echo "Domain: " . $domain . ".\n";
echo "Email: " . $email . ".\n";

$val_domain = validate_domains($domain);
if (count($val_domain['errors']) >= 1 ) {
$errors = $val_domain['errors'];
foreach ($errors as $error_value) {
echo "\t" . $error_value . ". \n";
}
send_error_mail($domain, $email, $errors);
$json_a[$key]['errors'] += 1;
$check_json = json_encode($json_a);
if(file_put_contents($check_file, $check_json, LOCK_EX)) {
echo "\tError count updated to " . $json_a[$key]['errors'] . ".\n";
} else {
echo "Can't write database.\n";
continue;
}
if ($json_a[$key]['errors'] >= 7) {
echo "\tToo many errors. Adding domain to removal queue.\n";
$removal_queue[] = $key;
}
continue;
}
$raw_chain = get_raw_chain($domain);
$counter = 0;
foreach ($raw_chain['chain'] as $chain_key => $chain_value) {
$counter += 1;
$cert_exp_date = cert_expiry_date($chain_value);
$cert_cn = cert_cn($chain_value);
$cert_expiry = cert_expiry($chain_value);

echo "\tCert Chain #" . $counter . ". Expiry Date: " . date("Y-m-d H:i:s T", $cert_exp_date) . ". Common Name: " . $cert_cn . "\n";

cert_expiry_emails($domain, $email, $cert_expiry, $chain_value);

}
$file = file_get_contents($check_file);
if ($file === FALSE) {
echo "\tCan't open database.\n";
continue;
}
$json_a = json_decode($file, true);
if ($json_a === null && json_last_error() !== JSON_ERROR_NONE) {
echo "\tCan't read database\n";
continue;
}
if ($json_a[$key]['errors'] != 0) {
$json_a[$key]['errors'] = 0;
$check_json = json_encode($json_a);
if(file_put_contents($check_file, $check_json, LOCK_EX)) {
echo "\tError count reset to 0.\n";
} else {
echo "Can't write database.\n";
}
}

echo "\n";

}

if ( count($removal_queue) != 0 ) {
echo "Processing removal queue.\n";
foreach ($removal_queue as $remove_key => $remove_value) {
$unsub_url = "http://" . $current_domain . "/unsubscribe.php?id=" . $remove_value;
$file = file_get_contents($unsub_url);
if ($file === FALSE) {
echo "\tRemoval Error.\n";
continue;
} else {
echo "\tRemoved $remove_value.\n";
}
}
}

// remove non-confirmed subs older than 7 days
$tmp_pre_check_file = $pre_check_file . ".tmp";
if (!copy($pre_check_file, $tmp_pre_check_file)) {
echo "Failed to copy $pre_check_file to $tmp_pre_check_file.\n";
}

$tmp_pre_file = file_get_contents($tmp_pre_check_file);
if ($tmp_pre_file === FALSE) {
echo "Can't open database.\n";
}
$tmp_pre_json_a = json_decode($tmp_pre_file, true);
if ($tmp_pre_json_a === null && json_last_error() !== JSON_ERROR_NONE) {
echo "Can't read database.\n";
}

if (count($tmp_pre_json_a) == 0) {
echo "Empty pre-checklist.\n";
exit;
}

foreach ($tmp_pre_json_a as $pre_key => $pre_value) {
$today = strtotime(date("Y-m-d"));
$pre_add_date = strtotime(date("Y-m-d",$pre_value['pre_add_date']));
$pre_add_diff = $today - $pre_add_date;
if ($pre_add_diff > "604800") {
unset($tmp_pre_json_a[$pre_key]);
$tmp_pre_json = json_encode($tmp_pre_json_a);
if(file_put_contents($pre_check_file, $tmp_pre_json, LOCK_EX)) {
echo "Subscription for " . $pre_value['domain'] . " from " . $pre_value['email'] . " older than 7 days. Removing from subscription list.\n";
} else {
echo "Failed to remove subscription for " . $pre_value['domain'] . " from " . $pre_value['email'] . " older than 7 days from subscription list.\n";
}
}
}

} else {
header('HTTP/1.0 301 Moved Permanently');
header('Location: /');
}

?>
5 changes: 5 additions & 0 deletions css/bootstrap.min.css

Large diffs are not rendered by default.

Empty file added css/index.html
Empty file.
27 changes: 27 additions & 0 deletions css/ssl.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
Copyright (C) 2015 Remy van Elst
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

.footer {
position: absolute;
bottom: 0;
width: 100%;
padding-top: 5px;
/* Set the fixed height of the footer here */
border-top: 1px gray dashed;
height: 35px;
background-color: #fff;
}
Binary file added fonts/glyphicons-halflings-regular.ttf
Binary file not shown.
Binary file added fonts/glyphicons-halflings-regular.woff
Binary file not shown.
Empty file added fonts/index.html
Empty file.
Loading

0 comments on commit 786395f

Please sign in to comment.