Initial commit
This commit is contained in:
parent
fc9d72c6ae
commit
a652237205
2
LICENSE
2
LICENSE
@ -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
|
||||
|
32
README.md
32
README.md
@ -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.
|
||||
|
||||

|
||||
|
||||
## 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
103
certificates.js
Normal 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
6
css/bootstrap/bootstrap.min.css
vendored
Executable file
File diff suppressed because one or more lines are too long
1
css/bootstrap/bootstrap.min.css.map
Executable file
1
css/bootstrap/bootstrap.min.css.map
Executable file
File diff suppressed because one or more lines are too long
79
get_cert_info.js
Normal file
79
get_cert_info.js
Normal 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
31
index.html
Normal 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>
|
3
js/handlebars/handlebars-v4.0.5.min.js
vendored
Normal file
3
js/handlebars/handlebars-v4.0.5.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
4
js/jquery/jquery-2.2.2.min.js
vendored
Normal file
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
BIN
screenshot.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 251 KiB |
53
scripts.js
Normal file
53
scripts.js
Normal 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);
|
||||
|
||||
});
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user