Menu="OtherSettings"
Type="xmenu"
Title="Management Access"
Icon="icon-key"
Tag="expeditedssl"
---
<?PHP
/* Copyright 2005-2021, Lime Technology
 * Copyright 2012-2021, Bergware International.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version 2,
 * as published by the Free Software Foundation.
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 */
?>
<?
function find_tasks() {
  global $site;
  $tasks = [];
  foreach ($site as $page) {
    if (empty($page['Menu'])) continue;
    $menu = strtok($page['Menu'], ' ');
    switch ($menu[0]) {
      case '$': $menu = get_ini_key($menu,strtok(' ')); break;
      case '/': $menu = get_file_key($menu,strtok(' ')); break;
    }
    while ($menu !== false) {
      if (substr($menu,0,5) == 'Tasks') {
        if (empty($page['Cond'])) $tasks[] = $page['name'];
        break;
      }
      $menu = strtok(' ');
    }
  }
  sort($tasks);
  return $tasks;
}
function acceptableCert($certFile, $hostname, $expectedURL) {
  if (!file_exists($certFile)) return false;
  $certURLs=null;
  // get Subject URL and all SAN URLs from cert
  exec("openssl x509 -noout -subject -nameopt multiline -in $certFile | sed -n 's/ *commonName *= //p' ; openssl x509 -noout -ext subjectAltName -in $certFile | grep -Eo \"DNS:[a-zA-Z 0-9.*-]*\" | sed \"s/DNS://g\"", $certURLs);
  foreach($certURLs as $certURL) {
    // adjust for wildcard certs
    $certURL = str_replace('*', $hostname, $certURL);
    // case-insensitive compare
    if (strcasecmp($certURL, $expectedURL) == 0) return true;
  }
  return false;
}
$tasks       = find_tasks();
$ethX        = 'eth0';
#$addr        = ipaddr($ethX);
$addr        = $_SERVER['SERVER_ADDR'];
$keyfile     = @file_get_contents($var['regFILE']);
if ($keyfile !== false) $keyfile = base64_encode($keyfile);

// self-signed or user-provided cert
$cert1File    = "/boot/config/ssl/certs/{$var['NAME']}_unraid_bundle.pem";
$cert1Present = file_exists("$cert1File");
if ($cert1Present) {
  $cert1URL = $var['NAME'].".".$var['LOCAL_TLD'];
  // if user replaced cert without reloading nginx, the cert on the flash could be invalid
  $cert1URLvalid = acceptableCert($cert1File, $var['NAME'], $cert1URL);
  $cert1Issuer  = exec("/usr/bin/openssl x509 -in $cert1File -noout -text | sed -n -e 's/^.*Issuer: //p'");
  $cert1Expires = exec("/usr/bin/openssl x509 -in $cert1File -noout -text | sed -n -e 's/^.*Not After : //p'");
}

// unraid.net, myunraid.net LE cert. could potentially be user provided as well
$cert2File    = "/boot/config/ssl/certs/certificate_bundle.pem";
$cert2Present = file_exists("$cert2File");
if ($cert2Present) {
  $cert2Subject = exec("/usr/bin/openssl x509 -in $cert2File -noout -subject -nameopt multiline 2>/dev/null|sed -n 's/ *commonName *= //p'");
  $cert2Issuer  = exec("/usr/bin/openssl x509 -in $cert2File -noout -text | sed -n -e 's/^.*Issuer: //p'");
  $cert2Expires = exec("/usr/bin/openssl x509 -in $cert2File -noout -text | sed -n -e 's/^.*Not After : //p'");
  $isLegacyCert = preg_match('/.*\.unraid\.net$/', $cert2Subject);
  $isWildcardCert = preg_match('/.*\.myunraid\.net$/', $cert2Subject);
  $isLEcert = $isLegacyCert || $isWildcardCert;
  $subject2URL = $cert2Subject;
  if ($isWildcardCert) {
    if (strpos($addr, ":") === false)
      $subject2URL = str_replace("*", str_replace(".", "-", $addr), $subject2URL);
    else
      $subject2URL = str_replace("*", str_replace(":", "-", $addr), $subject2URL);
  }
  if ($isLEcert) {
    exec("openssl x509 -checkend 2592000 -noout -in $cert2File 2>/dev/null", $arrout, $retval_expired);
    if (strpos($addr, ":") === false) {
      $rebindtest_ip = exec("host -4 -t A rebindtest4.myunraid.net 2>/dev/null|awk '{print \$4}'");
      $dnsRebindingProtection = ($rebindtest_ip != "192.168.42.42");
      if (!$dnsRebindingProtection) {
        $cert_ip = exec("host -4 -t A $subject2URL 2>/dev/null|awk '{print \$4}'");
        $dnsValid = $cert_ip==$addr;
      }
    } else {
      $rebindtest_ip = exec("host -6 -t AAAA rebindtest6.myunraid.net 2>/dev/null|awk '{print \$4}'");
      // more: restore this after TTL expires
      // $dnsRebindingProtection = ($rebindtest_ip != "fd42::42");
      $dnsRebindingProtection = ($rebindtest_ip != "fd42::42") && ($rebindtest_ip != "fd42::");
      if (!$dnsRebindingProtection) {
        $cert_ip = exec("host -6 -t AAAA $subject2URL 2>/dev/null|awk '{print \$4}'");
        $dnsValid = $cert_ip==$addr;
      }
    }
  }
}

$nginx = parse_ini_file('/var/local/emhttp/nginx.ini');
$http_port = $var['PORT'] != 80 ? ":{$var['PORT']}" : '';
$https_port = $var['PORTSSL'] != 443 ? ":{$var['PORTSSL']}" : '';

$http_ip_url = "http://{$nginx['NGINX_LANIP']}{$http_port}/";
$https_ip_url = "https://{$nginx['NGINX_LANIP']}{$https_port}/";
$http_ip6_url = "http://{$nginx['NGINX_LANIP6']}{$http_port}/";
$https_ip6_url = "https://{$nginx['NGINX_LANIP6']}{$https_port}/";
$http_mdns_url = "http://{$nginx['NGINX_LANMDNS']}{$http_port}/";
$https_mdns_url = "https://{$nginx['NGINX_LANMDNS']}{$https_port}/";
$https_fqdn_url = "https://{$nginx['NGINX_LANFQDN']}{$https_port}/";
$https_fqdn6_url = "https://{$nginx['NGINX_LANFQDN6']}{$https_port}/";

$urls = array();
// push an array of three values into the $urls array:
//   0 - the url
//   1 - the url it redirects to, or null
//   2 - the certificate file used, or null
switch($var['USE_SSL']) {
  case 'no':
    if ($nginx['NGINX_LANIP'])    $urls[] = [$http_ip_url, null, null];
    if ($nginx['NGINX_LANIP6'])   $urls[] = [$http_ip6_url, null, null];
    if ($nginx['NGINX_LANMDNS'])  $urls[] = [$http_mdns_url, null, null];
    if ($nginx['NGINX_LANFQDN'])  $urls[] = [$https_fqdn_url, null, "certificate_bundle.pem"];
    if ($nginx['NGINX_LANFQDN6']) $urls[] = [$https_fqdn6_url, null, "certificate_bundle.pem"];
    break;
  case 'yes':
    if ($nginx['NGINX_LANIP'])    $urls[] = [$http_ip_url, $https_ip_url, null];
    if ($nginx['NGINX_LANIP'])    $urls[] = [$https_ip_url, null, "{$var['NAME']}_unraid_bundle.pem"];
    if ($nginx['NGINX_LANIP6'])   $urls[] = [$http_ip6_url, $https_ip6_url, null];
    if ($nginx['NGINX_LANIP6'])   $urls[] = [$https_ip6_url, null, "{$var['NAME']}_unraid_bundle.pem"];
    if ($nginx['NGINX_LANMDNS'])  $urls[] = [$http_mdns_url, $https_mdns_url, null];
    if ($nginx['NGINX_LANMDNS'])  $urls[] = [$https_mdns_url, null, "{$var['NAME']}_unraid_bundle.pem"];
    if ($nginx['NGINX_LANFQDN'])  $urls[] = [$https_fqdn_url, null, "certificate_bundle.pem"];
    if ($nginx['NGINX_LANFQDN6']) $urls[] = [$https_fqdn6_url, null, "certificate_bundle.pem"];
    break;
  case 'auto': // aka strict
    if ($nginx['NGINX_LANIP'])    $urls[] = [$http_ip_url, $https_fqdn_url, null];
    if ($nginx['NGINX_LANIP6'])   $urls[] = [$http_ip6_url, $https_fqdn6_url, null];
    if ($nginx['NGINX_LANMDNS'])  $urls[] = [$http_mdns_url, $https_fqdn_url, null];
    if ($nginx['NGINX_LANFQDN'])  $urls[] = [$https_fqdn_url, null, "certificate_bundle.pem"];
    if ($nginx['NGINX_LANFQDN6']) $urls[] = [$https_fqdn6_url, null, "certificate_bundle.pem"];
    break;
}

$cert_time_format   = $display['date'].($display['date']!='%c' ? ', '.str_replace(['%M','%R'],['%M:%S','%R:%S'],$display['time']):'');
$provisionlabel     = $isLEcert ? _('Renew') : _('Provision');
$disabled_provision = $keyfile===false || ($isLEcert && $retval_expired===0) ? 'disabled' : '';
$disabled_updatedns = $keyfile!==false && $isLEcert ? '' : 'disabled';
$disabled_delete    = $cert2Present && $var['USE_SSL']!='auto' ? '' : 'disabled';
$disabled_auto      = $isLEcert && !$dnsRebindingProtection && $dnsValid ? '' : 'disabled';
$upgradelabel       = _('Upgrade Cert');

// Get ports in use
$portsInUse = [];
exec("lsof -Pni|awk '/LISTEN/ && \$9!~/127.0.0.1/ && \$9!~/\\[::1\\]/{print \$9}'|sort -u", $output);

$bind = false; //= $var['BIND_MGT']=='yes'; - not used in 6.10
$list = ['*',$addr];

foreach ($output as $line) {
  [$ip, $port] = my_explode($line[0]=='[' ? ']:' : ':',$line);
  if ($ip[0]=='[') $ip = substr($ip,1); // ipv6 address
  if (!in_array($port,$portsInUse) && (!$bind || in_array($ip,$list))) $portsInUse[] = $port;
}
sort($portsInUse,SORT_NUMERIC);
?>
<script>
function provisionHandler(event, form) { // provisions and renewals require being Signed In to Unraid.net
  if (event.submitter.value === 'Delete') return true; // always allow deletes
  if (event.submitter.value === 'Renew') return true; // always allow renewals
};

function updateDNS(button) {
  $(button).prop("disabled", true).html("<i class='fa fa-circle-o-notch fa-spin fa-fw'></i>_(Update DNS)_");
  var failure = function(data) {
    var status = data.status;
    var obj = data.responseJSON;
    var msg = "_(Sorry, an error occurred updating unraid.net DNS records)_. _(The error is)_: "+obj.error+".";
    $(button).prop("disabled", false).html("_(Update DNS)_");
    swal({title:"_(Oops)_",text:msg,type:"error",html:true,confirmButtonText:"_(Ok)_"});
  };
  var success = function(data) {
    $(button).prop("disabled", false).html("_(Update DNS)_");
    <?$text = _('Your local IP address %s has been updated for unraid.net')?>
    swal({title:"",text:"<?=sprintf($text,$addr)?>",type:"success",html:true,confirmButtonText:"_(Ok)_"});
  };
  $.post("/webGui/include/UpdateDNS.php",success).fail(failure);
}

function checkPorts(form) {
  var portsInUse = [<?=implode(',',$portsInUse)?>];
  var range = [], list = [], duplicates = [];
  var checks = [
    {'key':"PORTTELNET", 'used':"<?=$var['USE_TELNET']=='yes'?>", 'port':"<?=$var['PORTTELNET']?>", 'default':"23" , 'name':"_(TELNET port)_"},
    {'key':"PORTSSH"   , 'used':"<?=$var['USE_SSH']=='yes'?>"   , 'port':"<?=$var['PORTSSH']?>"   , 'default':"22" , 'name':"_(SSH port)_"},
    {'key':"PORT"      , 'used':"<?=$var['USE_SSL']=='no'?>"    , 'port':"<?=$var['PORT']?>"      , 'default':"80" , 'name':"_(HTTP port)_"},
    {'key':"PORTSSL"   , 'used':"<?=$var['USE_SSL']!='no'?>"    , 'port':"<?=$var['PORTSSL']?>"   , 'default':"443", 'name':"_(HTTPS port)_"}
  ];
  for (var i=0,check; check=checks[i]; i++) {
    var item = $(form).find('input[name="'+check['key']+'"]');
    if ($(item).is(':disabled')) continue;
    item.val(parseInt(item.val()));
    var current = (check['port']||check['default']).toString();
    var userPort = (item.val()||check['default']).toString();
    if (userPort < 1 || userPort > 65535) range.push(userPort+' ('+check['name']+')');
    duplicates.push(userPort);
    if (check['used'] == '1' && userPort != current) {
      if (portsInUse.includes(parseInt(userPort))) list.push(userPort+' ('+check['name']+')');
    }
  }
  duplicates = duplicates.reduce(function(acc, el, i, arr) {
    if (arr.indexOf(el) !== i && acc.indexOf(el) < 0) acc.push(el); return acc;
  }, []);

  if (range.length > 0) {
    swal({title:'_(Port out of range)_',text:sprintf('_(Port %s is out of range (minimum 1 maximum 65535))_',range.join(', ')),type:'error',showCancelButton:false,confirmButtonText:"_(OK)_"});
    return;
  }
  if (list.length > 0) {
    swal({title:'_(Port already in use)_',text:sprintf('_(Port %s is already in use by other services)_',list.join(', ')),type:'error',showCancelButton:false,confirmButtonText:"_(OK)_"});
    return;
  }
  if (duplicates.length > 0) {
    swal({title:'_(Duplicate port entered)_',text:sprintf('_(Port %s is duplicated)_',duplicates.join(', ')),type:'error',showCancelButton:false,confirmButtonText:"_(OK)_"});
    return;
  }
  form.submit();
}

function updateTELNET(form) {
  form.PORTTELNET.disabled = form.USE_TELNET.value=='no';
}

function updateSSH(form) {
  form.PORTSSH.disabled = form.USE_SSH.value=='no';
}


$(function(){
  var form = document.SSLSettings;
  updateTELNET(form);
  updateSSH(form);
});
</script>
<form markdown="1" name="SSLSettings" method="POST" action="/update.htm" target="progressFrame">
<input type="hidden" name="changePorts" value="Apply">
<input type="hidden" name="server_name" value="<?=strtok($_SERVER['HTTP_HOST'],":")?>">
<input type="hidden" name="server_addr" value="<?=$_SERVER['SERVER_ADDR']?>">
_(User 'root')_:
: [_(Manage)_](/Settings/Users/ManagementAccess/UserEdit?name=root)

_(Start page)_:
: <select name="START_PAGE">
<?foreach ($tasks as $task) echo mk_option($var['START_PAGE']??'Main', $task, _($task));?>
  </select>

:mgmt_start_page_help:

<!--
Restrict access:
: <select name="BIND_MGT" class="narrow">
  <?=mk_option($var['BIND_MGT'], "no", _('No'))?>
  <?=mk_option($var['BIND_MGT'], "yes", _('Yes'))?>
  </select>

> By default GUI, SSH and TELNET access are available on all active interfaces of the system.
>
> *Restrict access* limits GUI, SSH and TELNET access to the management interface only (eth0).
-->

_(Use TELNET)_:
: <select name="USE_TELNET" onchange="updateTELNET(this.form)">
  <?=mk_option($var['USE_TELNET'], "no", _('No'))?>
  <?=mk_option($var['USE_TELNET'], "yes", _('Yes'))?>
  </select>

:mgmt_use_telnet_help:

_(TELNET port)_:
: <input type="number" name="PORTTELNET" class="narrow" min="1" max="65535" value="<?=$var['PORTTELNET']?>" placeholder="23">

:mgmt_telnet_port_help:

_(Use SSH)_:
: <select name="USE_SSH" onchange="updateSSH(this.form)">
  <?=mk_option($var['USE_SSH'], "no", _('No'))?>
  <?=mk_option($var['USE_SSH'], "yes", _('Yes'))?>
  </select>

:mgmt_use_ssh_help:

_(SSH port)_:
: <input type="number" name="PORTSSH" class="narrow" min="1" max="65535" value="<?=$var['PORTSSH']?>" placeholder="22">

:mgmt_ssh_port_help:

_(Use UPnP)_:
: <select name="USE_UPNP">
  <?=mk_option($var['USE_UPNP'], "yes", _('Yes'))?>
  <?=mk_option($var['USE_UPNP'], "no", _('No'))?>
  </select>

:mgmt_use_upnp_help:

_(Use SSL/TLS)_:
: <select name="USE_SSL">
  <?=mk_option($var['USE_SSL'], "no", _('No'))?>
  <?=mk_option($var['USE_SSL'], "yes", _('Yes'))?>
  <?=mk_option($var['USE_SSL'], "auto", _('Strict'), $disabled_auto)?>
  </select>

:mgmt_use_ssl_tls_help:

_(HTTP port)_:
: <input type="number" name="PORT" class="narrow" min="1" max="65535" value="<?=$var['PORT']?>" placeholder="80">

:mgmt_http_port_help:

_(HTTPS port)_:
: <input type="number" name="PORTSSL" class="narrow" min="1" max="65535" value="<?=$var['PORTSSL']?>" placeholder="443">

:mgmt_https_port_help:

_(Local TLD)_:
: <input type="text" name="LOCAL_TLD" class="narrow" value="<?=htmlspecialchars($var['LOCAL_TLD'])?>">

:mgmt_local_tld_help:

&nbsp;
: <input type="button" value="_(Apply)_" onclick="checkPorts(this.form)" disabled><input type="button" value="_(Done)_" onclick="done()">

</form>

<div style="height:24px"></div>
<form markdown="1">

<?
echo "<dl><dt>"._("Local Access URLs").":</dt>\n";
echo "<dd><br><ul>\n";
foreach($urls as $url) {
  if ($url[1]) $msg = " ("._("redirects to")." <a href='{$url[1]}'>{$url[1]}</a>)";
  if ($url[2]) $msg = " ("._("uses certificate")." {$url[2]} )";
  echo "<li><a href='{$url[0]}'>{$url[0]}</a>{$msg}</li>\n";
}
echo "</ul></dd></dl>\n";
?>

:mgmt_local_access_urls_help:

</form>

<div style="height:24px"></div>
<form markdown="1">
<?if ($cert1Present):?>
_(Self-signed or user-provided certificate)_:
: <?=$cert1File?>

<?if ($cert1URLvalid && $var['USE_SSL']=='yes'):?>
_(Certificate URL)_:
: [<?=$cert1URL?>](https://<?=$cert1URL?><?=$https_port?>)
<?elseif ($cert1URLvalid):?>
_(Certificate URL)_:
: <?=$cert1URL?>
<?else:?>
_(Certificate URL)_:
: _(Certificate not valid for)_ <?=$cert1URL?>
<?endif;?>

_(Certificate issuer)_:
: <?=$cert1Issuer?>

_(Certificate expiration)_:
: <?=_(strftime($cert_time_format, strtotime($cert1Expires)),0)?>

<?else:?>
_(Self-signed certificate file)_:
: _(Not present)_
<?endif;?>

</form>

<div style="height:24px"></div>
<form markdown="1" name="Provision" method="POST" target="progressFrame" action="/update.htm" onsubmit="provisionHandler(event, this)">
<input type="hidden" name="server_name" value="<?=strtok($_SERVER['HTTP_HOST'],":")?>">
<input type="hidden" name="server_addr" value="<?=$_SERVER['SERVER_ADDR']?>">
<?if ($cert2Present):?>
_(Unraid Let's Encrypt certificate)_:
: <?=$cert2File?>

_(Certificate URL)_:
: [<?=$cert2Subject?>](https://<?=$subject2URL?><?=$https_port?>)

_(Certificate issuer)_:
: <?=$cert2Issuer?>

_(Certificate expiration)_:
: <?=_(strftime($cert_time_format, strtotime($cert2Expires)),0)?>

<?if ($dnsRebindingProtection):?>
&nbsp;
: _(DNS Rebinding Protection is ENABLED)_

<?elseif ($dnsValid !== true):?>
&nbsp;
: _(DNS Propagation is PENDING)_

<?endif;?>
<?else:?>
_(CA-signed certificate file)_:
: _(Not present)_

<?endif;?>
&nbsp;
: <button type="submit" name="changePorts" value="Provision" <?=$disabled_provision?>><?=$provisionlabel?></button><button type="submit" name="changePorts" value="Delete" <?=$disabled_delete?> >_(Delete)_</button><!-- <button type="button" onclick="updateDNS(this)" <?=$disabled_updatedns?>>_(Update DNS)_</button> -->

<?if ($cert2Present && $isLegacyCert):?>
&nbsp;
: <button type="submit" name="changePorts" value="Upgrade"><?=$upgradelabel?></button>
<?endif;?>

:mgmt_certificate_expiration_help:

</form>
