Initial commit

This commit is contained in:
Craine Runton 2016-05-17 16:25:53 -06:00
parent fc9d72c6ae
commit a652237205
11 changed files with 312 additions and 2 deletions

View File

@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2016 Craine
Copyright (c) 2016 Craine Runton
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -1,2 +1,32 @@
# tls-dashboard
A dashboard written in JavaScript & HTML to check the remaining time before a TLS certificate expires.
A dashboard written in JavaScript & HTML to check the remaining time before a TLS certificate expires. A combination of a Node script and an HTML/CSS/JS webpage to display the info.
## Usage
### `get_cert_info.js`
This is the node script that establishes a connection to the sites that you want/need to monitor, and reads info about the peer certificate. It then parses the certificate, and dumps the relevant data into another .js file (`certificates.js`) for use on the status page.
To setup the module, go to line 7, and switch out the contents of the array with all the hosts you're wanting to monitor:
var host_list = [
'www.google.com',
'www.twitter.com',
'www.github.com',
'www.bitbucket.com',
'news.ycombinator.com',
'barnacl.es'
];
If you're hosting this somewhere and want a regularly updating dashboard, set it up on a cron job and pass the path to your webroot as an argument to the script. e.g. `node get_cert_info.js /path/to/web/root`, and it will dump the `certificates.js` file there. Otherwise, you can keep it local, run the script when you want updated info, and just load `index.html` locally.
## Example
Take a look at a live example page [here on GitLab][1]. Screenshot is below.
![Example dashboard](https://raw.githubusercontent.com/cmrunton/tls-dashboard/master/screenshot.png)
## TODO
1. Comment the module functions
2. Separate the host_list into another file that can be read and versioned on its own (useful for large deployments, if you're managing 1,000s of certs)
3. Error handling for when the connection fails (right now the script hangs/exits with an uncaught error)
[1]:https://pages.runtondev.com/tls-dashboard/

103
certificates.js Normal file
View File

@ -0,0 +1,103 @@
var run_date = 'Tue May 17 2016';
var cert_info = {
"1": {
"server": "www.google.com",
"subject": {
"org": "Google Inc",
"common_name": "www.google.com",
"sans": "DNS:www.google.com"
},
"issuer": {
"org": "Google Inc",
"common_name": "Google Internet Authority G2"
},
"info": {
"valid_from": "2016-05-04T09:17:02.000Z",
"valid_to": "2016-07-27T08:39:00.000Z",
"days_left": 70
}
},
"2": {
"server": "news.ycombinator.com",
"subject": {
"common_name": "*.ycombinator.com",
"sans": "DNS:*.ycombinator.com, DNS:ycombinator.com"
},
"issuer": {
"org": "COMODO CA Limited",
"common_name": "COMODO RSA Domain Validation Secure Server CA"
},
"info": {
"valid_from": "2014-08-22T00:00:00.000Z",
"valid_to": "2019-08-21T23:59:59.000Z",
"days_left": 1191
}
},
"3": {
"server": "www.twitter.com",
"subject": {
"org": "Twitter, Inc.",
"common_name": "twitter.com",
"sans": "DNS:twitter.com, DNS:www.twitter.com"
},
"issuer": {
"org": "DigiCert Inc",
"common_name": "DigiCert SHA2 Extended Validation Server CA"
},
"info": {
"valid_from": "2016-03-09T00:00:00.000Z",
"valid_to": "2018-03-14T12:00:00.000Z",
"days_left": 666
}
},
"4": {
"server": "www.github.com",
"subject": {
"org": "GitHub, Inc.",
"common_name": "github.com",
"sans": "DNS:github.com, DNS:www.github.com"
},
"issuer": {
"org": "DigiCert Inc",
"common_name": "DigiCert SHA2 Extended Validation Server CA"
},
"info": {
"valid_from": "2016-03-10T00:00:00.000Z",
"valid_to": "2018-05-17T12:00:00.000Z",
"days_left": 730
}
},
"5": {
"server": "barnacl.es",
"subject": {
"common_name": "www.barnacl.es",
"sans": "DNS:www.barnacl.es, DNS:barnacl.es"
},
"issuer": {
"org": "StartCom Ltd.",
"common_name": "StartCom Class 1 DV Server CA"
},
"info": {
"valid_from": "2016-04-13T20:08:11.000Z",
"valid_to": "2017-04-13T20:08:11.000Z",
"days_left": 331
}
},
"6": {
"server": "www.bitbucket.com",
"subject": {
"org": "Atlassian, Inc.",
"common_name": "*.bitbucket.com",
"sans": "DNS:*.bitbucket.com, DNS:bitbucket.com"
},
"issuer": {
"org": "DigiCert Inc",
"common_name": "DigiCert SHA2 High Assurance Server CA"
},
"info": {
"valid_from": "2015-04-10T00:00:00.000Z",
"valid_to": "2017-05-10T12:00:00.000Z",
"days_left": 358
}
}
}

6
css/bootstrap/bootstrap.min.css vendored Executable file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

79
get_cert_info.js Normal file
View File

@ -0,0 +1,79 @@
var https = require('https');
const fs = require('fs');
const directory = process.argv[2]+'/';
const file_name = 'certificates.js';
const run_date = new Date().toDateString();
var host_list = [
'www.google.com',
'www.twitter.com',
'www.github.com',
'www.bitbucket.com',
'news.ycombinator.com',
'barnacl.es'
];
var output = {};
var iteration = 1;
host_list.forEach(get_cert_parameters)
function get_cert_parameters(element, index, array) {
var options = {
host: element,
port: 443,
method: 'GET'
};
var req = https.request(options, function(res) {
var cert = res.connection.getPeerCertificate();
var parsed = {
'server': element,
'subject': {
'org': cert.subject.O,
'common_name': cert.subject.CN,
'sans': cert.subjectaltname
},
'issuer': {
'org': cert.issuer.O,
'common_name': cert.issuer.CN
},
'info': {
'valid_from': parse_date(cert.valid_from),
'valid_to': parse_date(cert.valid_to),
'days_left': get_days_left(cert.valid_to)
}
}
add_cert_details(parsed, iteration);
check_iterations();
});
req.end();
};
function parse_date(date_string) {
var date = new Date(Date.parse(date_string));
return date;
};
function get_days_left(date_string) {
var now = Date.now();
var then = new Date(Date.parse(date_string));
var days_left = Math.round((then - now)/86400000);
return days_left;
};
function add_cert_details(object, host) {
output[host] = object;
};
function check_iterations() {
if (iteration === host_list.length) {
write_results();
assert(true, iteration+' of '+host_list.length+' urls complete.');
} else {
console.log(iteration+' of '+host_list.length+' urls complete.');
iteration++;
}
};
function write_results() {
fs.writeFile(directory+file_name, 'var run_date = \''+run_date+'\'; \nvar cert_info = '+JSON.stringify(output, null, 2), function(err) {
// If the write errored out, notify
if (err) {
console.log('Error writing file. \n'+stats);
}
})
};

31
index.html Normal file
View File

@ -0,0 +1,31 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>SSL Dashboard</title>
<meta content="Version 1.0">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="./css/bootstrap/bootstrap.min.css" type="text/css"/>
<script src="./js/jquery/jquery-2.2.2.min.js" ></script>
<script src="./js/handlebars/handlebars-v4.0.5.min.js" ></script>
<script src="./certificates.js" ></script>
<script src="./scripts.js" ></script>
</head>
<body>
<div class="container-fluid">
<div class="row">
<div class="col-xs-12 text-center bg-primary"><h1>TLS Certificate Expiration Dashboard</h1></div>
<div class="col-xs-12 text-center bg-primary"><h3><small>Created: <span id="created_date"></span></small></h3></div>
</div>
<div class="row" style="margin-top: 1rem;">
<div class="col-xs-12 col-lg-10 col-lg-push-1">
<div id="panel" class="card-deck">
</div>
</div>
</div>
</div>
</body>
</html>

File diff suppressed because one or more lines are too long

4
js/jquery/jquery-2.2.2.min.js vendored Normal file

File diff suppressed because one or more lines are too long

BIN
screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 KiB

53
scripts.js Normal file
View File

@ -0,0 +1,53 @@
$(function () {
$('#created_date').html(run_date);
var sorted_certificates = Object.keys(cert_info)
.sort(function( a, b ) {
return cert_info[a].info.days_left - cert_info[b].info.days_left;
}).map(function(sortedKey) {
return cert_info[sortedKey];
});
var card_html = String()
+'<div class="col-xs-12 col-md-6 col-xl-4">'
+' <div class="card text-xs-center" style="border-color:#333;">'
+' <div class="card-header" style="overflow:hidden;">'
+' <h4 class="text-muted" style="margin-bottom:0;">{{server}}</h4>'
+' </div>'
+' <div class="card-block {{background}}">'
+' <h1 class="card-text display-4" style="margin-top:0;margin-bottom:-1rem;">{{days_left}}</h1>'
+' <p class="card-text" style="margin-bottom:.75rem;"><small>days left</small></p>'
+' </div>'
+' <div class="card-footer">'
+' <h6 class="text-muted" style="margin-bottom:.5rem;">Issued by: {{issuer}}</h6>'
+' <h6 class="text-muted" style=""><small>{{issuer_cn}}</small></h6>'
+' <h6 class="text-muted" style="margin-bottom:0;"><small>{{common_name}}</small></h6>'
+' </div>'
+' </div>'
+'</div>';
function insert_card(json) {
var card_template = Handlebars.compile(card_html),
html = card_template(json);
$('#panel').append(html);
};
sorted_certificates.forEach(function(element, index, array){
var json = {
'server': element.server,
'days_left': element.info.days_left,
'issuer': element.issuer.org,
'common_name': element.subject.common_name,
'issuer_cn': element.issuer.common_name
}
if (element.info.days_left <= 30 ){
json.background = 'card-inverse card-danger';
} else if (element.info.days_left > 30 && element.info.days_left <= 60 ) {
json.background = 'card-inverse card-warning';
} else {
json.background = 'card-inverse card-success';
}
insert_card(json);
});
});