mirror of
				https://github.com/mail-in-a-box/mailinabox.git
				synced 2025-11-03 19:30:54 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			285 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			285 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
<style>
 | 
						|
</style>
 | 
						|
 | 
						|
<h2>TLS (SSL) Certificates</h2>
 | 
						|
 | 
						|
<p>A TLS (formerly called SSL) certificate is a cryptographic file that proves to anyone connecting to a web address that the connection is secure between you and the owner of that address.</p>
 | 
						|
 | 
						|
<p>You need a TLS certificate for this box’s hostname ({{hostname}}) and every other domain name and subdomain that this box is hosting a website for (see the list below).</p>
 | 
						|
 | 
						|
<div id="ssl_provision">
 | 
						|
<h3>Provision a Certificate</h3>
 | 
						|
 | 
						|
<div id="ssl_provision_p" style="display: none; margin-top: 1.5em">
 | 
						|
  <button onclick='return provision_tls_cert();' class='btn btn-primary' style="float: left; margin: 0 1.5em 1em 0;">Provision</button>
 | 
						|
  <p>A TLS certificate can be automatically provisioned from <a href="https://letsencrypt.org/" target="_blank">Let’s Encrypt</a>, a free TLS certificate provider, for:<br>
 | 
						|
  <span class="text-primary"></span></p>
 | 
						|
</div>
 | 
						|
 | 
						|
<div class="clearfix"> </div>
 | 
						|
 | 
						|
<div id="ssl_provision_result"></div>
 | 
						|
 | 
						|
<div id="ssl_provision_problems_div" style="display: none;">
 | 
						|
<p style="margin-bottom: .5em;">Certificates cannot be automatically provisioned for:</p>
 | 
						|
<table id="ssl_provision_problems" style="margin-top: 0;" class="table">
 | 
						|
  <thead>
 | 
						|
    <tr>
 | 
						|
      <th>Domain</th>
 | 
						|
      <th>Problem</th>
 | 
						|
    </tr>
 | 
						|
  </thead>
 | 
						|
  <tbody>
 | 
						|
  </tbody>
 | 
						|
</table>
 | 
						|
<p>Use the <em>Install Certificate</em> button below for these domains.</p>
 | 
						|
</div>
 | 
						|
</div>
 | 
						|
 | 
						|
<h3>Certificate Status</h3>
 | 
						|
 | 
						|
<p style="margin-top: 1.5em">Certificates expire after a period of time. All certificates will be automatically renewed through <a href="https://letsencrypt.org/" target="_blank">Let’s Encrypt</a> 14 days prior to expiration.</p>
 | 
						|
 | 
						|
<table id="ssl_domains" class="table" style="margin-bottom: 2em; width: auto; display: none">
 | 
						|
  <thead>
 | 
						|
    <tr>
 | 
						|
      <th>Domain</th>
 | 
						|
      <th>Certificate Status</th>
 | 
						|
      <th>Actions</th>
 | 
						|
    </tr>
 | 
						|
  </thead>
 | 
						|
  <tbody>
 | 
						|
  </tbody>
 | 
						|
</table>
 | 
						|
 | 
						|
 | 
						|
<h3 id="ssl_install_header">Install Certificate</h3>
 | 
						|
 | 
						|
<p>There are many other places where you can get a free or cheap certificate. If you don't want to use our automatic Let's Encrypt integration, you can give <a href="https://www.namecheap.com/security/ssl-certificates/domain-validation.aspx">Namecheap’s $9 certificate</a>, <a href="https://www.startssl.com/">StartSSL’s free express lane</a>, <a href="https://buy.wosign.com/free/">WoSign’s free TLS</a></a> or any other certificate provider a try.</p>
 | 
						|
 | 
						|
<p>Which domain are you getting a certificate for?</p>
 | 
						|
 | 
						|
<p><select id="ssldomain" onchange="show_csr()" class="form-control" style="width: auto"></select></p>
 | 
						|
 | 
						|
<p>(A multi-domain or wildcard certificate will be automatically applied to any domains it is valid for besides the one you choose above.)</p>
 | 
						|
 | 
						|
<p>What country are you in? This is required by some TLS certificate providers. You may leave this blank if you know your TLS certificate provider doesn't require it.</p>
 | 
						|
 | 
						|
<p><select id="sslcc" onchange="show_csr()" class="form-control" style="width: auto">
 | 
						|
	<option value="">(Select)</option>
 | 
						|
	{% for code, name in csr_country_codes %}
 | 
						|
		<option value="{{code}}">{{name}}</option>
 | 
						|
	{% endfor %}
 | 
						|
</select></p>
 | 
						|
 | 
						|
<div id="csr_info" style="display: none">
 | 
						|
  <p>You will need to provide the certificate provider this Certificate Signing Request (CSR):</p>
 | 
						|
 | 
						|
  <pre id="ssl_csr"></pre>
 | 
						|
 | 
						|
  <p><small>The CSR is safe to share. It can only be used in combination with a secret key stored on this machine.</small></p>
 | 
						|
 | 
						|
  <p>The certificate provider will then provide you with a TLS/SSL certificate. They may also provide you with an intermediate chain. Paste each separately into the boxes below:</p>
 | 
						|
 | 
						|
  <p style="margin-bottom: .5em">TLS/SSL certificate:</p>
 | 
						|
  <p><textarea id="ssl_paste_cert" class="form-control" style="max-width: 40em; height: 8em" placeholder="-----BEGIN CERTIFICATE-----
stuff here
-----END CERTIFICATE-----"></textarea></p>
 | 
						|
 | 
						|
  <p style="margin-bottom: .5em">TLS/SSL intermediate chain (if provided):</p>
 | 
						|
  <p><textarea id="ssl_paste_chain" class="form-control" style="max-width: 40em; height: 8em" placeholder="-----BEGIN CERTIFICATE-----
stuff here
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
more stuff here
-----END CERTIFICATE-----"></textarea></p>
 | 
						|
 | 
						|
  <p>After you paste in the information, click the install button.</p>
 | 
						|
 | 
						|
  <button class="btn-primary" onclick="install_cert()">Install</button>
 | 
						|
</div>
 | 
						|
 | 
						|
<script>
 | 
						|
function show_tls(keep_provisioning_shown) {
 | 
						|
 api(
 | 
						|
    "/ssl/status",
 | 
						|
    "GET",
 | 
						|
    {
 | 
						|
    },
 | 
						|
    function(res) {
 | 
						|
      // provisioning status
 | 
						|
 | 
						|
      if (!keep_provisioning_shown)
 | 
						|
        $('#ssl_provision').toggle(res.can_provision.length + res.cant_provision.length > 0)
 | 
						|
 | 
						|
      $('#ssl_provision_p').toggle(res.can_provision.length > 0);
 | 
						|
      if (res.can_provision.length > 0)
 | 
						|
          $('#ssl_provision_p span').text(res.can_provision.join(", "));
 | 
						|
      
 | 
						|
      $('#ssl_provision_problems_div').toggle(res.cant_provision.length > 0);
 | 
						|
      $('#ssl_provision_problems tbody').text("");
 | 
						|
      for (var i = 0; i < res.cant_provision.length; i++) {
 | 
						|
        var domain = res.cant_provision[i];
 | 
						|
        var row = $("<tr><th class='domain'><a href=''></a></th><td class='status'></td></tr>");
 | 
						|
        $('#ssl_provision_problems tbody').append(row);
 | 
						|
        row.attr('data-domain', domain.domain);
 | 
						|
        row.find('.domain a').text(domain.domain);
 | 
						|
        row.find('.domain a').attr('href', 'https://' + domain.domain);
 | 
						|
        row.find('.status').text(domain.problem);
 | 
						|
      }
 | 
						|
 | 
						|
      // certificate status
 | 
						|
      var domains = res.status;
 | 
						|
      var tb = $('#ssl_domains tbody');
 | 
						|
      tb.text('');
 | 
						|
      $('#ssldomain').html('<option value="">(select)</option>');
 | 
						|
      $('#ssl_domains').show();
 | 
						|
      for (var i = 0; i < domains.length; i++) {
 | 
						|
        var row = $("<tr><th class='domain'><a href=''></a></th><td class='status'></td> <td class='actions'><a href='#' onclick='return ssl_install(this);' class='btn btn-xs'>Install Certificate</a></td></tr>");
 | 
						|
        tb.append(row);
 | 
						|
        row.attr('data-domain', domains[i].domain);
 | 
						|
        row.find('.domain a').text(domains[i].domain);
 | 
						|
        row.find('.domain a').attr('href', 'https://' + domains[i].domain);
 | 
						|
        if (domains[i].status == "not-applicable") {
 | 
						|
          domains[i].status = "muted"; // text-muted css class
 | 
						|
          row.find('.actions a').remove(); // no actions applicable
 | 
						|
        }
 | 
						|
        row.addClass("text-" + domains[i].status);
 | 
						|
        row.find('.status').text(domains[i].text);
 | 
						|
        if (domains[i].status == "success") {
 | 
						|
          row.find('.actions a').addClass('btn-default').text('Replace Certificate');
 | 
						|
        } else {
 | 
						|
          row.find('.actions a').addClass('btn-primary').text('Install Certificate');
 | 
						|
        }
 | 
						|
 | 
						|
        $('#ssldomain').append($('<option>').text(domains[i].domain));
 | 
						|
      }
 | 
						|
    });
 | 
						|
}
 | 
						|
 | 
						|
function ssl_install(elem) {
 | 
						|
  var domain = $(elem).parents('tr').attr('data-domain');
 | 
						|
  $('#ssldomain').val(domain);
 | 
						|
  show_csr();
 | 
						|
  $('html, body').animate({ scrollTop: $('#ssl_install_header').offset().top - $('.navbar-fixed-top').height() - 20 })
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
function show_csr() {
 | 
						|
 if ($('#ssldomain').val() == "") return;
 | 
						|
 $('#csr_info').slideDown();
 | 
						|
 $('#ssl_csr').text('Loading...');
 | 
						|
 api(
 | 
						|
    "/ssl/csr/" + $('#ssldomain').val(),
 | 
						|
    "POST",
 | 
						|
    {
 | 
						|
        countrycode: $('#sslcc').val()
 | 
						|
    },
 | 
						|
    function(data) {
 | 
						|
      $('#ssl_csr').text(data);
 | 
						|
    });
 | 
						|
}
 | 
						|
 | 
						|
function install_cert() {
 | 
						|
 api(
 | 
						|
    "/ssl/install",
 | 
						|
    "POST",
 | 
						|
    {
 | 
						|
      domain: $('#ssldomain').val(),
 | 
						|
      cert: $('#ssl_paste_cert').val(),
 | 
						|
      chain: $('#ssl_paste_chain').val()
 | 
						|
    },
 | 
						|
    function(status) {
 | 
						|
      if (/^OK($|\n)/.test(status)) {
 | 
						|
        console.log(status)
 | 
						|
        show_modal_error("TLS Certificate Installation", "Certificate has been installed. Check that you have no connection problems to the domain.", function() { show_ssl(); $('#csr_info').slideUp(); });
 | 
						|
      } else {
 | 
						|
        show_modal_error("TLS Certificate Installation", status);
 | 
						|
      }
 | 
						|
    });
 | 
						|
}
 | 
						|
 | 
						|
var agree_to_tos_url_prompt = null;
 | 
						|
var agree_to_tos_url = null;
 | 
						|
function provision_tls_cert() {
 | 
						|
  // Automatically provision any certs.
 | 
						|
  $('#ssl_provision_p .btn').attr('disabled', '1'); // prevent double-clicks
 | 
						|
  api(
 | 
						|
    "/ssl/provision",
 | 
						|
    "POST",
 | 
						|
    {
 | 
						|
      agree_to_tos_url: agree_to_tos_url
 | 
						|
    },
 | 
						|
    function(status) {
 | 
						|
      // Clear last attempt.
 | 
						|
      agree_to_tos_url = null;
 | 
						|
      $('#ssl_provision_result').text("");
 | 
						|
      may_reenable_provision_button = true;
 | 
						|
 | 
						|
      // Nothing was done. There might also be problem domains, but we've already displayed those.
 | 
						|
      if (status.requests.length == 0) {
 | 
						|
        show_modal_error("TLS Certificate Provisioning", "There were no domain names to provision certificates for.");
 | 
						|
        // don't return - haven't re-enabled the provision button
 | 
						|
      }
 | 
						|
 | 
						|
      // Each provisioning API call returns zero or more "requests" which represent
 | 
						|
      // a request to Let's Encrypt for a single certificate. Normally there is just
 | 
						|
      // one request (for a single multi-domain certificate).
 | 
						|
      for (var i = 0; i < status.requests.length; i++) {
 | 
						|
        var r = status.requests[i];
 | 
						|
 | 
						|
        // create an HTML block to display the results of this request
 | 
						|
        var n = $("<div><h4/><p/></div>");
 | 
						|
        $('#ssl_provision_result').append(n);
 | 
						|
 | 
						|
        // show a header only to disambiguate request blocks
 | 
						|
        if (status.requests.length > 0)
 | 
						|
          n.find("h4").text(r.domains.join(", "));
 | 
						|
 | 
						|
        if (r.result == "agree-to-tos") {
 | 
						|
          // user needs to agree to Let's Encrypt's TOS
 | 
						|
          agree_to_tos_url_prompt = r.url;
 | 
						|
          $('#ssl_provision_p .btn').attr('disabled', '1');
 | 
						|
          n.find("p").html("Please open and review <a href='" + r.url + "' target='_blank'>Let's Encrypt's terms of service agreement</a>. You must agree to their terms for a certificate to be automatically provisioned from them.");
 | 
						|
          n.append($('<button onclick="agree_to_tos_url = agree_to_tos_url_prompt; return provision_tls_cert();" class="btn btn-success" style="margin-left: 2em">Agree & Try Again</button>'));
 | 
						|
 | 
						|
          // don't re-enable the Provision button -- user must use the Agree button
 | 
						|
          may_reenable_provision_button = false;
 | 
						|
 | 
						|
        } else if (r.result == "error") {
 | 
						|
          n.find("p").addClass("text-danger").text(r.message);
 | 
						|
 | 
						|
        } else if (r.result == "wait") {
 | 
						|
          // Show a button that counts down to zero, at which point it becomes enabled.
 | 
						|
          n.find("p").text("A certificate is now in the process of being provisioned, but it takes some time. Please wait until the Finish button is enabled, and then click it to acquire the certificate.");
 | 
						|
          var b = $('<button onclick="return provision_tls_cert();" class="btn btn-success" style="margin-left: 2em">Finish</button>');
 | 
						|
          b.attr("disabled", "1");
 | 
						|
          var now = new Date();
 | 
						|
          n.append(b);
 | 
						|
          function ready_to_finish() {
 | 
						|
            var remaining = Math.round(r.seconds - (new Date() - now)/1000);
 | 
						|
            if (remaining > 0) {
 | 
						|
              setTimeout(ready_to_finish, 1000);
 | 
						|
              b.text("Finish (" + remaining + "...)")
 | 
						|
            } else {
 | 
						|
              b.text("Finish (ready)")
 | 
						|
              b.removeAttr("disabled");
 | 
						|
            }
 | 
						|
          }
 | 
						|
          ready_to_finish();
 | 
						|
          
 | 
						|
          // don't re-enable the Provision button -- user must use the Retry button when it becomes enabled
 | 
						|
          may_reenable_provision_button = false;
 | 
						|
 | 
						|
        } else if (r.result == "installed") {
 | 
						|
          n.find("p").addClass("text-success").text("The TLS certificate was provisioned and installed.");
 | 
						|
          setTimeout("show_tls(true)", 1); // update main table of certificate statuses, call with arg keep_provisioning_shown true so that we don't clear what we just outputted
 | 
						|
        }
 | 
						|
        
 | 
						|
        // display the detailed log info in case of problems
 | 
						|
        var trace = $("<div class='small text-muted' style='margin-top: 1.5em'>Log:</div>");
 | 
						|
        n.append(trace);
 | 
						|
        for (var j = 0; j < r.log.length; j++)
 | 
						|
          trace.append($("<div/>").text(r.log[j]));
 | 
						|
 | 
						|
      }
 | 
						|
 | 
						|
      if (may_reenable_provision_button)
 | 
						|
        $('#ssl_provision_p .btn').removeAttr("disabled");
 | 
						|
    });
 | 
						|
}
 | 
						|
</script>
 |