|
@@ -1,12 +1,183 @@
|
|
|
|
|
|
+<h3><i class="ui lock icon"></i> TLS / SSL Certificates</h3>
|
|
|
+<p>Setup TLS cert for different domains of your reverse proxy server names</p>
|
|
|
+<div class="ui divider"></div>
|
|
|
+<h4>Default Certificates</h4>
|
|
|
+ <small>When there are no matching certificate for the requested server name, reverse proxy router will always fallback to this one.<br>Note that you need both of them uploaded for it to fallback properly</small></p>
|
|
|
+ <table class="ui very basic celled table">
|
|
|
+ <thead>
|
|
|
+ <tr><th>Key Type</th>
|
|
|
+ <th>Exists</th>
|
|
|
+ </tr></thead>
|
|
|
+ <tbody>
|
|
|
+ <tr>
|
|
|
+ <td><i class="globe icon"></i> Default Public Key</td>
|
|
|
+ <td id="pubkeyExists"></td>
|
|
|
+ </tr>
|
|
|
+ <tr>
|
|
|
+ <td><i class="lock icon"></i> Default Private Key</td>
|
|
|
+ <td id="prikeyExists"></td>
|
|
|
+ </tr>
|
|
|
+ </tbody>
|
|
|
+ </table>
|
|
|
+<button class="ui button" onclick="uploadPublicKey();"><i class="globe icon"></i> Upload Public Key</button>
|
|
|
+<button class="ui black button" onclick="uploadPrivateKey();"><i class="lock icon"></i> Upload Private Key</button>
|
|
|
+<div class="ui divider"></div>
|
|
|
+<h4>Sub-domain Certificates</h4>
|
|
|
+<p>Provide certificates for multiple domains reverse proxy</p>
|
|
|
+<div class="ui fluid form">
|
|
|
+ <div class="three fields">
|
|
|
+ <div class="field">
|
|
|
+ <label>Server Name (Domain)</label>
|
|
|
+ <input type="text" id="certdomain" placeholder="example.com / blog.example.com">
|
|
|
+ </div>
|
|
|
+ <div class="field">
|
|
|
+ <label>Public Key</label>
|
|
|
+ <input type="file" id="pubkeySelector" onchange="handleFileSelect(event, 'pub')">
|
|
|
+ </div>
|
|
|
+ <div class="field">
|
|
|
+ <label>Private Key</label>
|
|
|
+ <input type="file" id="prikeySelector" onchange="handleFileSelect(event, 'pri')">
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <button class="ui teal button" onclick="handleDomainKeysUpload();"><i class="ui upload icon"></i> Upload</button>
|
|
|
+</div>
|
|
|
+<div id="certUploadSuccMsg" class="ui green message" style="display:none;">
|
|
|
+ <i class="ui checkmark icon"></i> Certificate for domain <span id="certUploadingDomain"></span> uploaded.
|
|
|
+</div>
|
|
|
+<br>
|
|
|
+<div >
|
|
|
+ <table class="ui very basic celled table">
|
|
|
+ <thead>
|
|
|
+ <tr><th>Domain</th>
|
|
|
+ <th>Last Update</th>
|
|
|
+ <th>Remove</th>
|
|
|
+ </tr></thead>
|
|
|
+ <tbody id="certifiedDomainList">
|
|
|
|
|
|
-<button onclick="uploadPublicKey();">Upload</button>
|
|
|
-<button onclick="uploadPrivateKey();">Upload Private Key</button>
|
|
|
-
|
|
|
+ </tbody>
|
|
|
+ </table>
|
|
|
+</div>
|
|
|
+<div class="ui message">
|
|
|
+ <h4><i class="info circle icon"></i> Sub-domain Certificates</h4>
|
|
|
+ If you have 3rd or even 4th level subdomains like <code>blog.example.com</code> or <code>en.blog.example.com</code> ,
|
|
|
+ depending on your certificates coverage, you might need to setup them one by one (i.e. having two seperate certificate for <code>a.example.com</code> and <code>b.example.com</code>).<br>
|
|
|
+ If you have a wildcard certificate that covers <code>*.example.com</code>, you can just enter <code>example.com</code> as server name in the form below to add a certificate.
|
|
|
+</div>
|
|
|
<script>
|
|
|
+ var uploadPendingPublicKey = undefined;
|
|
|
+ var uploadPendingPrivateKey = undefined;
|
|
|
+
|
|
|
+ function initManagedDomainCertificateList(){
|
|
|
+ $("#certifiedDomainList").html("");
|
|
|
+ $.get("/cert/list?date=true", function(data){
|
|
|
+ if (data.error != undefined){
|
|
|
+ alert(data.error);
|
|
|
+ }else{
|
|
|
+ data.forEach(entry => {
|
|
|
+ $("#certifiedDomainList").append(`<tr>
|
|
|
+ <td>${entry.Domain}</td>
|
|
|
+ <td>${entry.LastModifiedDate}</td>
|
|
|
+ <td><button title="Delete key-pair" class="ui mini basic red icon button"><i class="ui red trash icon"></i></button></td>
|
|
|
+ </tr>`);
|
|
|
+ })
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ initManagedDomainCertificateList();
|
|
|
+
|
|
|
+ function handleDomainUploadByKeypress(){
|
|
|
+ handleDomainKeysUpload(function(){
|
|
|
+ $("#certUploadingDomain").text($("#certdomain").val().trim());
|
|
|
+ //After uploaded, reset the file selector
|
|
|
+ document.getElementById('pubkeySelector').value = '';
|
|
|
+ document.getElementById('prikeySelector').value = '';
|
|
|
+ document.getElementById('certdomain').value = '';
|
|
|
+
|
|
|
+ uploadPendingPublicKey = undefined;
|
|
|
+ uploadPendingPrivateKey = undefined;
|
|
|
+
|
|
|
+ //Show succ
|
|
|
+ $("#certUploadSuccMsg").stop().finish().slideDown("fast").delay(3000).slideUp("fast");
|
|
|
+ });
|
|
|
+ }
|
|
|
+ //Handle domain keys upload
|
|
|
+ function handleDomainKeysUpload(callback=undefined){
|
|
|
+ let domain = $("#certdomain").val();
|
|
|
+ if (domain.trim() == ""){
|
|
|
+ alert("Missing domain.");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (uploadPendingPublicKey && uploadPendingPrivateKey && typeof uploadPendingPublicKey === 'object' && typeof uploadPendingPrivateKey === 'object') {
|
|
|
+ const publicKeyForm = new FormData();
|
|
|
+ publicKeyForm.append('file', uploadPendingPublicKey, 'publicKey');
|
|
|
+
|
|
|
+ const privateKeyForm = new FormData();
|
|
|
+ privateKeyForm.append('file', uploadPendingPrivateKey, 'privateKey');
|
|
|
+
|
|
|
+ const publicKeyRequest = new XMLHttpRequest();
|
|
|
+ publicKeyRequest.open('POST', '/cert/upload?ktype=pub&domain=' + domain);
|
|
|
+ publicKeyRequest.onreadystatechange = function() {
|
|
|
+ if (publicKeyRequest.readyState === XMLHttpRequest.DONE) {
|
|
|
+ if (publicKeyRequest.status !== 200) {
|
|
|
+ alert('Error uploading public key: ' + publicKeyRequest.statusText);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (callback != undefined){
|
|
|
+ callback();
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ };
|
|
|
+ publicKeyRequest.send(publicKeyForm);
|
|
|
+
|
|
|
+ const privateKeyRequest = new XMLHttpRequest();
|
|
|
+ privateKeyRequest.open('POST', '/cert/upload?ktype=pri&domain=' + domain);
|
|
|
+ privateKeyRequest.onreadystatechange = function() {
|
|
|
+ if (privateKeyRequest.readyState === XMLHttpRequest.DONE) {
|
|
|
+ if (privateKeyRequest.status !== 200) {
|
|
|
+ alert('Error uploading private key: ' + privateKeyRequest.statusText);
|
|
|
+ }
|
|
|
+ if (callback != undefined){
|
|
|
+ callback();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+ privateKeyRequest.send(privateKeyForm);
|
|
|
+ } else {
|
|
|
+ alert('One or both of the files is missing or not a file object');
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //Handlers for selecting domain based key pairs
|
|
|
+ //ktype = {"pub" / "pri"}
|
|
|
+ function handleFileSelect(event, ktype="pub") {
|
|
|
+ const file = event.target.files[0];
|
|
|
+ //const fileNameInput = document.getElementById('selected-file-name');
|
|
|
+ if (ktype == "pub"){
|
|
|
+ uploadPendingPublicKey = file;
|
|
|
+ }else if (ktype == "pri"){
|
|
|
+ uploadPendingPrivateKey = file;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ //fileNameInput.value = file.name;
|
|
|
+ }
|
|
|
+
|
|
|
+ //Check if the default keypairs exists
|
|
|
+ function initDefaultKeypairCheck(){
|
|
|
+ $.get("/cert/checkDefault", function(data){
|
|
|
+ let tick = `<i class="ui green checkmark icon"></i>`;
|
|
|
+ let cross = `<i class="ui red times icon"></i>`;
|
|
|
+ $("#pubkeyExists").html(data.DefaultPubExists?tick:cross);
|
|
|
+ $("#prikeyExists").html(data.DefaultPriExists?tick:cross);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ initDefaultKeypairCheck();
|
|
|
+
|
|
|
function uploadPrivateKey(){
|
|
|
- // create file input element
|
|
|
- const input = document.createElement('input');
|
|
|
+ // create file input element
|
|
|
+ const input = document.createElement('input');
|
|
|
input.type = 'file';
|
|
|
|
|
|
// add change listener to file input
|
|
@@ -23,6 +194,7 @@
|
|
|
body: formData
|
|
|
})
|
|
|
.then(response => {
|
|
|
+ initDefaultKeypairCheck();
|
|
|
if (response.ok) {
|
|
|
alert('File upload successful!');
|
|
|
} else {
|
|
@@ -64,6 +236,7 @@
|
|
|
.then(response => {
|
|
|
if (response.ok) {
|
|
|
alert('File upload successful!');
|
|
|
+ initDefaultKeypairCheck();
|
|
|
} else {
|
|
|
response.text().then(text => {
|
|
|
alert(text);
|