Initial commit
This commit is contained in:
parent
fc9d72c6ae
commit
a652237205
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
|||||||
The MIT License (MIT)
|
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
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
32
README.md
32
README.md
@ -1,2 +1,32 @@
|
|||||||
# tls-dashboard
|
# 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