diff --git a/emhttp/languages/en_US/helptext.txt b/emhttp/languages/en_US/helptext.txt index 57d748267f..7b5527b511 100644 --- a/emhttp/languages/en_US/helptext.txt +++ b/emhttp/languages/en_US/helptext.txt @@ -2413,7 +2413,7 @@ The system attempted to determine the correct port automatically. If it used the In most cases this port is all you will need to specify in order to Serve the website in this container, although additional options are available below for more complex containers. This value is passed to the `` portion of this command which starts serve or funnel:
-`tailscale [serve|funnel] --bg -- http://localhost:`
+`tailscale [serve|funnel] --bg -- :`
For more details see the Tailscale Serve Command Line documentation. :end @@ -2421,27 +2421,34 @@ For more details see the ` portion of this command which starts serve or funnel:
+`tailscale [serve|funnel] --bg -- :`
+For more details see the
Tailscale Serve Command Line documentation.
+Please note that only `localhost` or `127.0.0.1` are supported. +:end + :docker_tailscale_serve_local_path_help: When not specified, this value defaults to an empty string. It is passed to the `` portion of this command which starts serve or funnel:
-`tailscale [serve|funnel] --bg -- http://localhost:`
+`tailscale [serve|funnel] --bg -- :`
For more details see the Tailscale Serve Command Line documentation. :end :docker_tailscale_serve_protocol_help: When not specified, this value defaults to "https". It is passed to the `` portion of this command which starts serve or funnel:
-`tailscale [serve|funnel] --bg --= http://localhost:`
+`tailscale [serve|funnel] --bg --= :`
For more details see the Tailscale Serve Command Line documentation. :end :docker_tailscale_serve_protocol_port_help: When not specified, this value defaults to "=443". It is passed to the `` portion of this command which starts serve or funnel:
-`tailscale [serve|funnel] --bg -- http://localhost:`
+`tailscale [serve|funnel] --bg -- :`
For more details see the Tailscale Serve Command Line documentation. :end :docker_tailscale_serve_path_help: When not specified, this value defaults to an empty string. It is passed to the `` portion of this command which starts serve or funnel:
-`tailscale [serve|funnel] --bg -- http://localhost:`
+`tailscale [serve|funnel] --bg -- :`
For more details see the Tailscale Serve Command Line documentation. :end diff --git a/emhttp/plugins/dynamix.docker.manager/include/ContainerManager.php b/emhttp/plugins/dynamix.docker.manager/include/ContainerManager.php index 90f2f770a1..2f95ff1146 100644 --- a/emhttp/plugins/dynamix.docker.manager/include/ContainerManager.php +++ b/emhttp/plugins/dynamix.docker.manager/include/ContainerManager.php @@ -1,6 +1,6 @@ getDockerContainers(); if (file_exists($user_prefs)) { $prefs = parse_ini_file($user_prefs); $sort = []; foreach ($containers as $ct) $sort[] = array_search($ct,$prefs) ?? 999; array_multisort($sort, ($action=='start'?SORT_ASC:SORT_DESC), SORT_NUMERIC, $containers); } - foreach ($containers as $ct) { + if ( $action == "start") { + $key = array_search($ct,array_column($info,"Name")); + if ( $key === false ) continue; + if ($info[$key]['NetworkMode'] == "host" && $info[$key]['Cmd'] == "/opt/unraid/tailscale") + continue; + } DockerUtil::docker("$action $ct >/dev/null"); addRoute($ct); } diff --git a/emhttp/plugins/dynamix.docker.manager/include/CreateDocker.php b/emhttp/plugins/dynamix.docker.manager/include/CreateDocker.php index 6f8d0a981a..9ae69149a9 100644 --- a/emhttp/plugins/dynamix.docker.manager/include/CreateDocker.php +++ b/emhttp/plugins/dynamix.docker.manager/include/CreateDocker.php @@ -1151,14 +1151,21 @@ function prepareCategory() { -
+
_(Use Tailscale)_: : onchange="showTailscale(this)"> :docker_tailscale_help:
+ +
+_(Use Tailscale)_: +: _(Option disabled as Network type is not bridge or custom)_ + +:docker_tailscale_help: +
_(NOTE)_: : _(This option will install Tailscale and dependencies into the container.)_ @@ -1300,6 +1307,14 @@ function prepareCategory() {
+
+_(Tailscale Serve Target)_: +: placeholder="_(Leave empty if unsure)_"> + +:docker_tailscale_serve_target_help: + +
+
_(Tailscale Serve Protocol)_: : placeholder="_(Leave empty if unsure, defaults to https)_"> @@ -1534,8 +1549,13 @@ function showSubnet(bridge) { $('#netCONT').val(''); } // make sure to re-trigger Tailscale check when network is changed - if ($('#contTailscale').prop('checked')) { - showTailscale(true); + if (bridge.match(/^(host|container)$/i) !== null) { + $('#contTailscale').siblings('.switch-button-background').click(); + $(".TSNetworkAllowed").hide(); + $(".TSNetworkNotAllowed").show(); + } else { + $(".TSNetworkAllowed").show(); + $(".TSNetworkNotAllowed").hide(); } } @@ -1665,6 +1685,7 @@ function showTSAdvanced(checked) { $('.TSservepath').hide(); $('.TSserveprotocol').hide(); $('.TSserveprotocolport').hide(); + $('.TSservetarget').hide(); $('.TSservelocalpath').hide(); $('.TSwebui').hide(); $('.TStroubleshooting').hide(); @@ -1678,6 +1699,7 @@ function showTSAdvanced(checked) { $('.TSservepath').show(); $('.TSserveprotocol').show(); $('.TSserveprotocolport').show(); + $('.TSservetarget').show(); $('.TSservelocalpath').show(); $('.TSwebui').show(); $('.TStroubleshooting').show(); @@ -1687,6 +1709,12 @@ function showTSAdvanced(checked) { } function showTailscale(source) { + var bridge = $('select[name="contNetwork"]').val(); + if (bridge.match(/^(host|container)$/i) !== null) { + $('#contTailscale').prop('checked',false); + $(".TSNetworkAllowed").hide(); + $(".TSNetworkNotAllowed").show(); + } if (!$.trim($('#TSallowlanaccess').val())) { $('#TSallowlanaccess').val('false'); } diff --git a/emhttp/plugins/dynamix.docker.manager/include/DockerClient.php b/emhttp/plugins/dynamix.docker.manager/include/DockerClient.php index b30b6d331b..b22685e0c8 100644 --- a/emhttp/plugins/dynamix.docker.manager/include/DockerClient.php +++ b/emhttp/plugins/dynamix.docker.manager/include/DockerClient.php @@ -389,16 +389,17 @@ public function getAllInfo($reload=false,$com=true,$communityApplications=false) $tmp['Project'] = $tmp['Project'] ?? $this->getTemplateValue($image, 'Project'); $tmp['DonateLink'] = $tmp['DonateLink'] ?? $this->getTemplateValue($image, 'DonateLink'); $tmp['ReadMe'] = $tmp['ReadMe'] ?? $this->getTemplateValue($image, 'ReadMe'); - if (empty($tmp['updated']) || $reload) { - if ($reload) $DockerUpdate->reloadUpdateStatus($image); - $tmp['updated'] = var_export($DockerUpdate->getUpdateStatus($image),true); - } if (!$com) $tmp['updated'] = 'undef'; - if ($ct['Manager'] !== 'dockerman') + if ($ct['Manager'] !== 'dockerman') { $tmp['template'] = null; - else if (empty($tmp['template']) || $reload) { + $tmp['updated'] = null; + } else if (empty($tmp['template']) || $reload) { $tmp['template'] = $this->getUserTemplate($name); if ($reload) $DockerUpdate->updateUserTemplate($name); + if (empty($tmp['updated']) || $reload) { + if ($reload) $DockerUpdate->reloadUpdateStatus($image); + $tmp['updated'] = var_export($DockerUpdate->getUpdateStatus($image),true); + } } //$this->debug("\n$name"); //foreach ($tmp as $c => $d) $this->debug(sprintf(' %-10s: %s', $c, $d)); diff --git a/emhttp/plugins/dynamix.docker.manager/include/DockerContainers.php b/emhttp/plugins/dynamix.docker.manager/include/DockerContainers.php index 267207e9dc..b1aec04991 100644 --- a/emhttp/plugins/dynamix.docker.manager/include/DockerContainers.php +++ b/emhttp/plugins/dynamix.docker.manager/include/DockerContainers.php @@ -195,10 +195,8 @@ function my_lang_log($text) { echo ""; } elseif (!empty($composestack)) { echo "
"._("Compose")."
"; - echo " "._('up-to-date').""; } else { echo "
"._("3rd Party")."
"; - echo " "._('up-to-date').""; } break; case 1: @@ -207,10 +205,8 @@ function my_lang_log($text) { echo " "._('apply update').""; } elseif (!empty($composestack)) { echo "
Compose
"; - echo " "._('update available').""; } else { echo "
3rd Party
"; - echo " "._('update available').""; } break; case 2: @@ -223,10 +219,8 @@ function my_lang_log($text) { echo ""; } elseif (!empty($composestack)) { echo "
"._("Compose")."
"; - echo " "._('not available').""; } else { echo "
"._("3rd Party")."
"; - echo " "._('not available').""; } break; } diff --git a/emhttp/plugins/dynamix.docker.manager/include/Events.php b/emhttp/plugins/dynamix.docker.manager/include/Events.php index a16c1d4184..75bbff32ab 100644 --- a/emhttp/plugins/dynamix.docker.manager/include/Events.php +++ b/emhttp/plugins/dynamix.docker.manager/include/Events.php @@ -1,6 +1,6 @@ $DockerClient->startContainer($container)]; + if ($container) { + $info = $DockerClient->getDockerContainers(); + $key = array_search($container,array_column($info,"Id")); + if ( $key === false ) { + $arrResponse = ['success' => _('Container not found. Try reloading this page to fix.')]; + break; + } + if ($info[$key]['NetworkMode'] == "host" && $info[$key]['Cmd'] == "/opt/unraid/tailscale") { + $arrResponse = ['success'=> _('For security reasons, containers with Network Type "Host" should not have Tailscale enabled. Please disable Tailscale in this container or change the Network Type of the container.')]; + break; + } + $arrResponse = ['success' => $DockerClient->startContainer($container)]; + } break; case 'pause': if ($container) $arrResponse = ['success' => $DockerClient->pauseContainer($container)]; diff --git a/emhttp/plugins/dynamix.docker.manager/include/Helpers.php b/emhttp/plugins/dynamix.docker.manager/include/Helpers.php index 28dab85c60..037852c1df 100644 --- a/emhttp/plugins/dynamix.docker.manager/include/Helpers.php +++ b/emhttp/plugins/dynamix.docker.manager/include/Helpers.php @@ -117,6 +117,7 @@ function postToXML($post, $setOwnership=false) { $xml->TailscaleWebUI = xml_encode(generateTSwebui($post['TSwebui'], $post['TSserve'], $post['contWebUI'])); if (isset($post['TSserve']) && strtolower($post['TSserve']) !== 'no') { $xml->TailscaleServePort = xml_encode($post['TSserveport']); + $xml->TailscaleServeTarget = xml_encode($post['TSservetarget']); $xml->TailscaleServeLocalPath = xml_encode($post['TSservelocalpath']); $xml->TailscaleServeProtocol = xml_encode($post['TSserveprotocol']); $xml->TailscaleServeProtocolPort = xml_encode($post['TSserveprotocolport']); @@ -171,6 +172,7 @@ function xmlToVar($xml) { $out['TailscaleUserspaceNetworking'] = xml_decode($xml->TailscaleUserspaceNetworking ?? ''); $out['TailscaleServe'] = xml_decode($xml->TailscaleServe ?? ''); $out['TailscaleServePort'] = xml_decode($xml->TailscaleServePort ?? ''); + $out['TailscaleServeTarget'] = xml_decode($xml->TailscaleServeTarget ?? ''); $out['TailscaleServeLocalPath'] = xml_decode($xml->TailscaleServeLocalPath ?? ''); $out['TailscaleServeProtocol'] = xml_decode($xml->TailscaleServeProtocol ?? ''); $out['TailscaleServeProtocolPort'] = xml_decode($xml->TailscaleServeProtocolPort ?? ''); @@ -364,6 +366,7 @@ function xmlToCommand($xml, $create_paths=false) { $TS_state_dir = ''; $TS_serve_funnel = ''; $TS_serve_port = ''; + $TS_serve_target = ''; $TS_serve_local_path = ''; $TS_serve_protocol = ''; $TS_serve_protocol_port = ''; @@ -397,6 +400,7 @@ function xmlToCommand($xml, $create_paths=false) { } $TS_serve_funnel = ($xml['TailscaleServe'] == 'funnel') ? '-e TAILSCALE_FUNNEL=true' : ''; $TS_serve_port = !empty($xml['TailscaleServePort']) ? '-e TAILSCALE_SERVE_PORT=' . escapeshellarg($xml['TailscaleServePort']) : ''; + $TS_serve_target = !empty($xml['TailscaleServeTarget']) ? '-e TAILSCALE_SERVE_TARGET=' . escapeshellarg($xml['TailscaleServeTarget']) : ''; $TS_serve_local_path = !empty($xml['TailscaleServeLocalPath']) ? '-e TAILSCALE_SERVE_LOCALPATH=' . escapeshellarg($xml['TailscaleServeLocalPath']) : ''; $TS_serve_protocol = !empty($xml['TailscaleServeProtocol']) ? '-e TAILSCALE_SERVE_PROTOCOL=' . escapeshellarg($xml['TailscaleServeProtocol']) : ''; $TS_serve_protocol_port = !empty($xml['TailscaleServeProtocolPort']) ? '-e TAILSCALE_SERVE_PROTOCOL_PORT=' . escapeshellarg($xml['TailscaleServeProtocolPort']) : ''; @@ -474,8 +478,8 @@ function xmlToCommand($xml, $create_paths=false) { $pid_limit = ""; } - $cmd = sprintf($docroot.'/plugins/dynamix.docker.manager/scripts/docker create %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s', - $cmdName, $TS_entrypoint, $cmdNetwork, $cmdMyIP, $cmdCPUset, $pid_limit, $cmdPrivileged, implode(' -e ', $Variables), $TS_hostname, $TS_exitnode, $TS_exitnode_ip, $TS_lan_access, $TS_routes, $TS_accept_routes, $TS_ssh, $TS_userspace_networking, $TS_serve_funnel, $TS_serve_port, $TS_serve_local_path, $TS_serve_protocol, $TS_serve_protocol_port, $TS_serve_path, $TS_daemon_params, $TS_extra_params, $TS_state_dir, $TS_troubleshooting, $TS_postargs, implode(' -l ', $Labels), $TS_web_ui, $TS_hostname_label, implode(' -p ', $Ports), implode(' -v ', $Volumes), $TS_hook, $TS_cap, $TS_tundev, implode(' --device=', $Devices), $xml['ExtraParams'], escapeshellarg($xml['Repository']), $xml['PostArgs']); + $cmd = sprintf($docroot.'/plugins/dynamix.docker.manager/scripts/docker create %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s', + $cmdName, $TS_entrypoint, $cmdNetwork, $cmdMyIP, $cmdCPUset, $pid_limit, $cmdPrivileged, implode(' -e ', $Variables), $TS_hostname, $TS_exitnode, $TS_exitnode_ip, $TS_lan_access, $TS_routes, $TS_accept_routes, $TS_ssh, $TS_userspace_networking, $TS_serve_funnel, $TS_serve_port, $TS_serve_target, $TS_serve_local_path, $TS_serve_protocol, $TS_serve_protocol_port, $TS_serve_path, $TS_daemon_params, $TS_extra_params, $TS_state_dir, $TS_troubleshooting, $TS_postargs, implode(' -l ', $Labels), $TS_web_ui, $TS_hostname_label, implode(' -p ', $Ports), implode(' -v ', $Volumes), $TS_hook, $TS_cap, $TS_tundev, implode(' --device=', $Devices), $xml['ExtraParams'], escapeshellarg($xml['Repository']), $xml['PostArgs']); return [preg_replace('/\s\s+/', ' ', $cmd), $xml['Name'], $xml['Repository']]; } function stopContainer($name, $t=false, $echo=true) { diff --git a/emhttp/plugins/dynamix.docker.manager/scripts/docker_init b/emhttp/plugins/dynamix.docker.manager/scripts/docker_init index 27e7b363a1..8431d2a254 100755 --- a/emhttp/plugins/dynamix.docker.manager/scripts/docker_init +++ b/emhttp/plugins/dynamix.docker.manager/scripts/docker_init @@ -1,2 +1,36 @@ -#!/bin/bash +#!/usr/bin/php +load("/boot/config/plugins/dockerMan/templates-user/my-{$cont[0]}.xml")) { + $newAuto[] = $container; + continue; + } + if ( ($doc->getElementsByTagName("Network")->item(0)->nodeValue ?? false) == "host" ) { + if ( ($doc->getElementsByTagName("TailscaleEnabled")->item(0)->nodeValue ?? false) == true ) { + exec("logger ".escapeshellarg("Autostart disabled on {$cont[0]} due to tailscale integration with host network.")); + exec("logger ".escapeshellarg("This is a security risk due to the possibility of unauthenticated access to your server's GUI and resources")); + exec("/usr/local/emhttp/plugins/dynamix/scripts/notify -e 'Autostart Disabled' -s 'Autostart Disabled' -d ".escapeshellarg("Autostart disabled automatically on {$cont[0]}")." -m ".escapeshellarg("Autostart has been automatically disabled on {$cont[0]} due to a security issue with container on network type host and tailscale integration enabled. You should either switch the network type or disabled tailscale integration on this container")." -i 'alert' -l '/Docker'"); + $flag = true; + continue; + } + } + $newAuto[] = $container; +} +if ( $flag ) + file_put_contents("/var/lib/docker/unraid-autostart",implode("\n",$newAuto)."\n"); +?> diff --git a/emhttp/plugins/dynamix.plugin.manager/scripts/plugin b/emhttp/plugins/dynamix.plugin.manager/scripts/plugin index 3f1bc9272f..9475180032 100755 --- a/emhttp/plugins/dynamix.plugin.manager/scripts/plugin +++ b/emhttp/plugins/dynamix.plugin.manager/scripts/plugin @@ -1,6 +1,6 @@ #!/usr/bin/php -q diff --git a/emhttp/plugins/dynamix/ArrayOperation.page b/emhttp/plugins/dynamix/ArrayOperation.page index 0c79a61bd4..f0a8bcfe26 100644 --- a/emhttp/plugins/dynamix/ArrayOperation.page +++ b/emhttp/plugins/dynamix/ArrayOperation.page @@ -4,8 +4,8 @@ Tag="snowflake-o" Nchan="device_list,disk_load,parity_list" --- 0; $poolsOnly = (_var($var,'SYS_ARRAY_SLOTS') == 0 ) ? true : false; /* only one of $present, $missing, or $wrong will be true, or all will be false */ -$forced = $present = $wrong = false; -foreach ($disks as $disk) { - if (strpos(_var($disk,'fsType'),'luks:')!==false || (_var($disk,'fsType')=='auto' && strpos(_var($var,'defaultFsType'),'luks:')!==false)) $forced = true; - if (_var($disk,'luksState',0)==1) $present = true; - if (_var($disk,'luksState',0)==2) $missing = true; - if (_var($disk,'luksState',0)==3) $wrong = true; +$forced = $present = $missing = $wrong = false; + +foreach (luks_filter($disks) as $disk) { + $fsType = _var($disk,'fsType'); + $luks = str_starts_with($fsType,'luks:'); + $auto = $fsType == 'auto'; + if ($luks || ($auto && str_starts_with(_var($var,'defaultFsType'),'luks:'))) $forced = true; + if ($luks || $auto) switch (_var($disk,'luksState',0)) { + case 1: $present = true; break; + case 2: $missing = true; break; + case 3: $wrong = true; break; + } } + $encrypt = $forced || $present || $missing || $wrong; if ($forced && ($present || $missing || $wrong)) $forced = false; function check_encryption() { global $forced, $missing, $wrong; - if ($forced) $status = _('Enter new key'); - elseif ($missing) $status = _('Missing key'); - elseif ($wrong) $status = _('Wrong key'); - else return; + if ($forced) + $status = _('Enter new key'); + elseif ($missing) + $status = _('Missing key'); + elseif ($wrong) + $status = _('Wrong key'); + else + return; echo "",_('Encryption status').":$statuspermit reformat"; echo "",_('Encryption input').":"; echo ""; echo "",_('Keyfile'),":"; } + function maintenance_mode() { echo ""; echo ""; @@ -55,6 +66,7 @@ function maintenance_mode() { echo "",_('Maintenance mode')," - ",_('if checked, Start array but do not mount disks'),""; echo ""; } + function status_indicator() { global $var; switch (_var($var,'mdColor')) { @@ -65,15 +77,18 @@ function status_indicator() { } echo "$help"; } + function missing_cache() { global $disks; $missing = false; foreach (cache_filter($disks) as $disk) $missing |= (strpos(_var($disk,'status'),'_MISSING')!==false); return $missing; } + function resync($d) { return in_array($d,['P','Q']) ? 'Parity-Sync' : 'Data-Rebuild'; } + function print_error($error) { return sprintf(_('Finding **%s** error'.($error==1?'':'s')),$error?:'0'); } @@ -111,27 +126,33 @@ function toggle_state(device,name,action) { devices.stop(); $.post('/webGui/include/ToggleState.php',{device:device,name:name,action:action},function(){setTimeout(function(){devices.start();},1000);if (button) $(button).prop('disabled',false);}); } + function display_diskio() { if ($.cookie('diskio')===undefined) { - $('span.number').show(); $('span.diskio').hide(); - } else { $('span.diskio').show(); $('span.number').hide(); + } else { + $('span.number').show(); $('span.diskio').hide(); } } + function toggle_diskio(init) { if (!init) { if ($.cookie('diskio')===undefined) $.cookie('diskio','diskio',{expires:3650}); else $.removeCookie('diskio'); } if ($.cookie('diskio')===undefined) { - $('i.toggle').removeClass('fa-tachometer').addClass('fa-list'); - } else { $('i.toggle').removeClass('fa-list').addClass('fa-tachometer'); + $('#clearstats').addClass('hidden'); + } else { + $('i.toggle').removeClass('fa-tachometer').addClass('fa-list'); + $('#clearstats').removeClass('hidden'); } display_diskio(); } + function base64(str) { return window.btoa(unescape(encodeURIComponent(str))); } + function selectInput(form) { form.input.value = 'file'; @@ -175,19 +196,21 @@ function selectInput(form) { item.prop('disabled',!form.file.value); } } + function getFileContent(event,form) { var input = event.target; var reader = new FileReader(); reader.onload = function(){form.file.value=reader.result;selectInput(form);}; reader.readAsDataURL(input.files[0]); } -function prepareInput(form,button) { - if (button) button.disabled = true; + +function prepareInput(form,button,parityWarn) { + button.disabled = true; $.post('/webGui/include/Report.php',{cmd:'state',pools:''},function(state) { if (state.length==0) { $(form).append(''); if (form.input === undefined) { - form.submit(); + parityWarn ? parityWarning(form,button) : form.submit(); return; } form.input.disabled = true; @@ -216,11 +239,12 @@ function prepareInput(form,button) { data['file'] = form.file.value; $.post('/update.php',data,function(){form.submit();}); } else { - swal({title:"_(Wrong Pool State)_",text:state,type:'error',html:true,confirmButtonText:"_(Ok)_"}); + swal({title:"_(Wrong Pool State)_",text:state,type:'error',html:true,confirmButtonText:"_(Ok)_"},function(){button.disabled=false;}); } }); } -function parityWarning(form) { + +function parityWarning(form,button) { if (form.md_invalidslot.checked) { var text = "_(*Dual parity* valid requires **ALL** disks in their original slots)_"; @@ -230,12 +254,16 @@ function parityWarning(form) { } else { var text = "_(*Parity* disk(s) content will be overwritten)_"; } - swal({title:"_(Proceed to start)_",text:text,html:true,type:'warning',showCancelButton:true,confirmButtonText:"_(Proceed)_",cancelButtonText:"_(Cancel)_"},function(){prepareInput(form);}); + swal({title:"_(Proceed to start)_",text:text,html:true,type:'warning',showCancelButton:true,confirmButtonText:"_(Proceed)_",cancelButtonText:"_(Cancel)_"},function(confirmed){ + confirmed ? form.submit() : button.disabled=false; + }); } + function tab0() { $.removeCookie('one'); $.cookie('tab','tab0'); } + function stopArray(form) { $(form).append(''); @@ -244,6 +272,7 @@ function stopArray(form) { form.submit(); } + function stopParity(form,text) { $(form).append(''); @@ -252,6 +281,7 @@ function stopParity(form,text) { form.submit(); } + function pauseParity(form) { $.post('/webGui/include/ParityControl.php',{action:'pause'},function(){ $('#pauseButton').val("_(Resume)_").prop('disabled',true).prop('onclick',null).off('click').click(function(){resumeParity(form);}); @@ -259,6 +289,7 @@ function pauseParity(form) { form.submit(); }); } + function resumeParity(form) { $.post('/webGui/include/ParityControl.php',{action:'resume'},function(){ $('#pauseButton').val("_(Pause)_").prop('disabled',true).prop('onclick',null).off('click').click(function(){pauseParity(form);}); @@ -266,9 +297,11 @@ function resumeParity(form) { form.submit(); }); } + function parityHistory() { openChanges("parity_history", "_(Parity Operation History)_", "phistory"); } + function shutdown_now(form,cmd) { $(form).append(''); @@ -281,9 +314,11 @@ function shutdown_now(form,cmd) { form.submit(); } + function toggleApply(checked) { $('input[name="#apply"]').prop('disabled',!checked); } + $('.tabs').append(ctrl); if ($.cookie('tab')=='tab0') $('i.toggle').hide(); @@ -292,7 +327,6 @@ $('#tab'+$('input[name$="tabs"]').length).click(function(){tab0(); $('i.toggle') $('div[class=title]:not(":last, .disable_diskio")').each(function(){$(this).append(ctrl);}); $('.tooltip_diskio').tooltipster({delay:100,trigger:'custom',triggerOpen:{mouseenter:true},triggerClose:{click:false,scroll:true,mouseleave:true}}); -toggle_diskio(true); var mymonitor = new NchanSubscriber('/sub/mymonitor',{subscriber:'websocket'}); @@ -422,7 +456,9 @@ setTimeout(function(){paritymonitor.start();},5000); $(function(){ var form = document.arrayOps; if (form.input !== undefined) selectInput(form); + toggle_diskio(true); }); + function formatWarning(val) { if (val==true) { swal({ @@ -640,7 +676,7 @@ window.onunload = function(){ **_(Start)_** _(will record all disk information and bring the array on-line)_.
_(The array will be immediately available, but **unprotected** since *parity* has not been assigned)_. - **_(Stopped)_**. _(Configuration valid)_. + **_(Stopped)_**. _(Configuration valid)_. **_(Start)_** _(will record all disk information, bring the array on-line, and start Parity-Sync)_.
_(The array will be immediately available, but **unprotected** until Parity-Sync completes)_.
_(Parity is already valid)_. @@ -720,7 +756,7 @@ endswitch; - +
**_(Spin Up)_** _(will immediately spin up all disks)_.
**_(Spin Down)_** _(will immediately spin down all disks)_.
**_(Clear Stats)_** _(will immediately clear all disk statistics)_.
diff --git a/emhttp/plugins/dynamix/DeviceInfo.page b/emhttp/plugins/dynamix/DeviceInfo.page old mode 100755 new mode 100644 diff --git a/emhttp/plugins/dynamix/DiskSettings.page b/emhttp/plugins/dynamix/DiskSettings.page index a5cb7cdf80..892c8e2439 100644 --- a/emhttp/plugins/dynamix/DiskSettings.page +++ b/emhttp/plugins/dynamix/DiskSettings.page @@ -4,8 +4,8 @@ Icon="icon-disks" Tag="icon-disk" --- - form.oldluks.value = base64(form.oldtext.value); + form.oldluks.value = base64(form.oldtext.value.replace(/\\"/g,'"')); form.oldtext.disabled = true; form.oldfile.disabled = true; - form.newluks.value = base64(form.newtext.value); - form.newtext.disabled = true; - form.newcopy.disabled = true; - form.newfile.disabled = true; + var valid = new RegExp('^[ -~]+$'); + if (form.newinput.value == 'file') return true; + if (valid.test(form.newtext.value)) { + form.newluks.value = base64(form.newtext.value.replace(/\\"/g,'"')); + form.newtext.disabled = true; + form.newcopy.disabled = true; + form.newfile.disabled = true; + return true; + } else { + swal({ + title:"_(Printable Characters Only)_", + text:"_(Use **ASCII** characters from space ' ' to tilde '~')_
_(Otherwise use the **keyfile** method for UTF8 input)_", + html:true, + type:'error', + confirmButtonText:"_(Ok)_" + }); + return false; + } } + function getFileContent(event,form,file) { var input = event.target; var reader = new FileReader(); @@ -268,7 +283,7 @@ _(Default critical SSD temperature threshold)_ (°
_(Change encryption key)_
-
+ diff --git a/emhttp/plugins/dynamix/ShareEdit.page b/emhttp/plugins/dynamix/ShareEdit.page index 53c206b318..2a41849e3f 100644 --- a/emhttp/plugins/dynamix/ShareEdit.page +++ b/emhttp/plugins/dynamix/ShareEdit.page @@ -17,7 +17,7 @@ Tag="share-alt-square" done()"; - return; + exit; } $width = [123,300]; diff --git a/emhttp/plugins/dynamix/UserEdit.page b/emhttp/plugins/dynamix/UserEdit.page index b9d846184b..ad044a999e 100644 --- a/emhttp/plugins/dynamix/UserEdit.page +++ b/emhttp/plugins/dynamix/UserEdit.page @@ -17,7 +17,7 @@ Tag="user" done()"; - return; + exit; } $user = "/boot/config/plugins/dynamix/users/$name.png"; $void = ""; diff --git a/emhttp/plugins/dynamix/WG0.page b/emhttp/plugins/dynamix/WG0.page index addd0942ec..71a31743fa 100644 --- a/emhttp/plugins/dynamix/WG0.page +++ b/emhttp/plugins/dynamix/WG0.page @@ -1415,7 +1415,7 @@ _(Peer allowed IPs)_: :wg_peer_allowed_ips_help: _(Peer DNS server)_: -: " onchange="quickValidate(this);" pattern="" title="_(Comma separated list of IPv4 and IPv6 IP addresses)_" > +: " onchange="quickValidate(this);" pattern="" title="_(Comma separated list of IPv4 and IPv6 IP addresses)_" > :wg_peer_dns_server_help: @@ -1509,7 +1509,7 @@ _(Peer allowed IPs)_: :wg_peer_allowed_ips_help: _(Peer DNS server)_: -: +: :wg_peer_dns_server_help: diff --git a/emhttp/plugins/dynamix/WGX.page b/emhttp/plugins/dynamix/WGX.page index cfea1fa52c..a8f5a98d0e 100644 --- a/emhttp/plugins/dynamix/WGX.page +++ b/emhttp/plugins/dynamix/WGX.page @@ -359,7 +359,7 @@ _(Peer allowed IPs)_: :wg_peer_allowed_ips_help: _(Peer DNS server)_: -: " onchange="quickValidate(this);" pattern="" title="_(Comma separated list of IPv4 and IPv6 IP addresses)_"> +: " onchange="quickValidate(this);" pattern="" title="_(Comma separated list of IPv4 and IPv6 IP addresses)_"> :wg_peer_dns_server_help: @@ -451,7 +451,7 @@ _(Peer allowed IPs)_: :wg_peer_allowed_ips_help: _(Peer DNS server)_: -: +: :wg_peer_dns_server_help: diff --git a/emhttp/plugins/dynamix/include/Boot.php b/emhttp/plugins/dynamix/include/Boot.php index c2b6a69871..15e7c243a8 100644 --- a/emhttp/plugins/dynamix/include/Boot.php +++ b/emhttp/plugins/dynamix/include/Boot.php @@ -157,6 +157,9 @@ function power_on() { {$var['fsProgress']}" : "
 "; @@ -187,5 +190,10 @@ function power_on() { echo '
'; echo '
'; echo ''; +}else{ +?> +echo + diff --git a/emhttp/plugins/dynamix/include/Helpers.php b/emhttp/plugins/dynamix/include/Helpers.php index 7bdf736d90..a985174c4d 100644 --- a/emhttp/plugins/dynamix/include/Helpers.php +++ b/emhttp/plugins/dynamix/include/Helpers.php @@ -1,6 +1,6 @@ /dev/null")); if (!empty($realdisk)) { $dirname = str_replace('/mnt/user/', "/mnt/$realdisk/", $dirname); $parent = str_replace('/mnt/user/', "/mnt/$realdisk/", $parent); } } - $fstype = trim(shell_exec(" stat -f -c '%T' $parent")); + $fstype = trim(shell_exec(" stat -f -c '%T' $parent")); $rtncode = false; + write_logging("fstype:$fstype parent $parent dir name $dirname\n"); switch ($fstype) { case "zfs": - $zfsdataset = trim(shell_exec("zfs list -H -o name $parent")) ; - $zfsdataset .= str_replace($parent,"",$dirname); - if ($recursive) $rtncode=exec("zfs create -p \"$zfsdataset\"");else $rtncode=exec("zfs create \"$zfsdataset\""); - if (!$rtncode) mkdir($dirname, $permissions, $recursive); else chmod($zfsdataset,$permissions); + if (is_dir($parent.'/.zfs')) { + write_logging("ZFS Volume\n"); + $zfsdataset = trim(shell_exec("zfs list -H -o name $parent")); + write_logging("Shell $zfsdataset\n"); + $zfsdataset .= str_replace($parent,"",$dirname); + write_logging("Dataset $zfsdataset\n"); + $zfsoutput = array(); + if ($recursive) exec("zfs create -p \"$zfsdataset\"",$zfsoutput,$rtncode);else exec("zfs create \"$zfsdataset\"",$zfsoutput,$rtncode); + write_logging("Output: {$zfsoutput[0]} $rtncode"); + if ($rtncode == 0) write_logging( " ZFS Command OK\n"); else write_logging( "ZFS Command Fail\n"); + } else {write_logging("Not ZFS dataset\n");$rtncode = 1;} + if ($rtncode > 0) { mkdir($dirname, $permissions, $recursive); write_logging( "created dir:$dirname\n");} else chmod($zfsdataset,$permissions); break; case "btrfs": - if ($recursive) $rtncode=exec("btrfs subvolume create --parents \"$dirname\""); else $rtncode=exec("btrfs subvolume create \"$dirname\""); - if (!$rtncode) mkdir($dirname, $permissions, $recursive); else chmod($dirname,$permissions); + $btrfsoutput = array(); + if ($recursive) exec("btrfs subvolume create --parents \"$dirname\"",$btrfsoutput,$rtncode); else exec("btrfs subvolume create \"$dirname\"",$btrfsoutput,$rtncode); + if ($rtncode > 0) mkdir($dirname, $permissions, $recursive); else chmod($dirname,$permissions); break; default: mkdir($dirname, $permissions, $recursive); @@ -380,16 +402,22 @@ function my_rmdir($dirname) { return($return); } function get_realvolume($path) { - if (strpos($path,"/mnt/user/",0) === 0) - $reallocation = trim(shell_exec("getfattr --absolute-names --only-values -n system.LOCATION ".escapeshellarg($path)." 2>/dev/null")); + if (strpos($path,"/mnt/user/",0) === 0) + $reallocation = trim(shell_exec("getfattr --absolute-names --only-values -n system.LOCATION ".escapeshellarg($path)." 2>/dev/null")); else { $realexplode = explode("/",str_replace("/mnt/","",$path)); $reallocation = $realexplode[0]; } return $reallocation; } -function device_exists($name) -{ + +function write_logging($value) { + $debug = is_file("/tmp/my_mkdir_debug"); + if (!$debug) return; + file_put_contents('/tmp/my_mkdir_output', $value, FILE_APPEND); +} + +function device_exists($name) { global $disks,$devs; return (array_key_exists($name, $disks) && !str_contains(_var($disks[$name],'status'),'_NP')) || (array_key_exists($name, $devs)); } diff --git a/emhttp/plugins/dynamix/include/KeyUpload.php b/emhttp/plugins/dynamix/include/KeyUpload.php index 193af71f5b..dd2bce1cf9 100644 --- a/emhttp/plugins/dynamix/include/KeyUpload.php +++ b/emhttp/plugins/dynamix/include/KeyUpload.php @@ -1,6 +1,6 @@ diff --git a/emhttp/plugins/dynamix/include/Wrappers.php b/emhttp/plugins/dynamix/include/Wrappers.php index 37e4be2277..07e3ab0062 100644 --- a/emhttp/plugins/dynamix/include/Wrappers.php +++ b/emhttp/plugins/dynamix/include/Wrappers.php @@ -1,6 +1,6 @@ /dev/null | grep -Pom2 '^[wc]ctemp +: \K\d+'",$temp); - return [$temp[0]-273, $temp[1]-273]; + return count($temp) >= 2 ? [$temp[0]-273, $temp[1]-273] : [0, 0]; case 'cctemp': return exec("nvme id-ctrl /dev/$device 2>/dev/null | grep -Pom1 '^cctemp +: \K\d+'")-273; case 'wctemp': @@ -235,4 +234,7 @@ function check_network_connectivity(): bool { $out = http_get_contents($url); return ($out=="Microsoft NCSI"); } +function shieldarg(...$args) { + return implode(' ', array_map('escapeshellarg', $args)); +} ?> diff --git a/emhttp/plugins/dynamix/include/update.encryption.php b/emhttp/plugins/dynamix/include/update.encryption.php index 1bb9376469..eb49c36b6e 100644 --- a/emhttp/plugins/dynamix/include/update.encryption.php +++ b/emhttp/plugins/dynamix/include/update.encryption.php @@ -1,6 +1,6 @@ 1) exec("cryptsetup luksRemoveKey /dev/$disk $key &>/dev/null"); + if ($slots > 1) exec("cryptsetup luksRemoveKey ".shieldarg("/dev/$disk", $key)." &>/dev/null"); } + function diskname($name) { global $disks; - foreach ($disks as $disk) if (strncmp($name,$disk['device'],strlen($disk['device']))==0) return $disk['name']; + foreach ($disks as $disk) if (strncmp($name, $disk['device'], strlen($disk['device'])) == 0) return $disk['name']; return $name; } -function reply($text,$type) { - global $oldkey,$newkey,$delkey; + +function reply($text, $type) { + global $oldkey, $newkey; $reply = _var($_POST,'#reply'); - if (realpath(dirname($reply))=='/var/tmp') file_put_contents($reply,$text."\0".$type); + if (realpath(dirname($reply)) == '/var/tmp') file_put_contents($reply, $text."\0".$type); delete_file($oldkey); - if (_var($_POST,'newinput','text')=='text' || $delkey) delete_file($newkey); + if (_var($_POST,'newinput','text') == 'text') delete_file($newkey); die(); } if (isset($_POST['oldinput'])) { switch ($_POST['oldinput']) { case 'text': - file_put_contents($oldkey,base64_decode(_var($_POST,'oldluks'))); + file_put_contents($oldkey, base64_decode(_var($_POST,'oldluks'))); break; case 'file': - file_put_contents($oldkey,base64_decode(explode(';base64,',_var($_POST,'olddata','x;base64,'))[1])); + file_put_contents($oldkey, base64_decode(explode(';base64,',_var($_POST,'olddata','x;base64,'))[1])); break; } } else { - if (is_file($newkey)) copy($newkey,$oldkey); + if (is_file($newkey)) copy($newkey, $oldkey); } if (is_file($oldkey)) { $disk = $crypto[0]; // check first disk only (key is the same for all disks) - exec("cryptsetup luksOpen --test-passphrase --key-file $oldkey /dev/$disk &>/dev/null",$null,$error); + exec("cryptsetup luksOpen --test-passphrase --key-file ".shieldarg($oldkey, "/dev/$disk")." &>/dev/null", $null, $error); } else $error = 1; if ($error > 0) reply(_('Incorrect existing key'),'warning'); @@ -86,25 +88,25 @@ function reply($text,$type) { if (isset($_POST['newinput'])) { switch ($_POST['newinput']) { case 'text': - file_put_contents($newkey,base64_decode(_var($_POST,'newluks'))); + file_put_contents($newkey, base64_decode(_var($_POST,'newluks'))); $luks = 'luksKey'; - $data = _var($_POST,'newluks'); + $data = str_replace('+', '%2B', _var($_POST,'newluks')); break; case 'file': - file_put_contents($newkey,base64_decode(explode(';base64,',_var($_POST,'newdata','x;base64,'))[1])); + file_put_contents($newkey, base64_decode(explode(';base64,',_var($_POST,'newdata','x;base64,'))[1])); $luks = 'luksKey=&luksKeyfile'; $data = $newkey; break; } $good = $bad = []; foreach ($crypto as $disk) { - exec("cryptsetup luksAddKey --key-file $oldkey /dev/$disk $newkey &>/dev/null",$null,$error); - if ($error==0) $good[] = $disk; else $bad[] = diskname($disk); + exec("cryptsetup luksAddKey --key-file ".shieldarg($oldkey, "/dev/$disk", $newkey)." &>/dev/null", $null, $error); + if ($error == 0) $good[] = $disk; else $bad[] = diskname($disk); } - if (count($bad)==0) { + if (count($bad) == 0) { // all okay, remove the old key - foreach ($good as $disk) removeKey($oldkey,$disk); - exec("emcmd 'changeDisk=apply&$luks=$data'"); + foreach ($good as $disk) removeKey($oldkey, $disk); + exec("emcmd ".escapeshellarg("changeDisk=apply&$luks=$data")); reply(_('Key successfully changed'),'success'); } else { // something went wrong, restore key diff --git a/emhttp/plugins/dynamix/nchan/device_list b/emhttp/plugins/dynamix/nchan/device_list index e7b67feca3..4f221f9c8e 100755 --- a/emhttp/plugins/dynamix/nchan/device_list +++ b/emhttp/plugins/dynamix/nchan/device_list @@ -1,7 +1,7 @@ #!/usr/bin/php -q "._('Device to be encrypted').""; break; case 1: diff --git a/emhttp/plugins/dynamix/scripts/diagnostics b/emhttp/plugins/dynamix/scripts/diagnostics index fd728b59f8..3a88aa9d17 100755 --- a/emhttp/plugins/dynamix/scripts/diagnostics +++ b/emhttp/plugins/dynamix/scripts/diagnostics @@ -450,6 +450,9 @@ run("dmidecode -qt0 2>/dev/null|todos >>".escapeshellarg("/$diag/system/motherbo run("cat /proc/meminfo 2>/dev/null|todos >".escapeshellarg("/$diag/system/meminfo.txt")); run("dmidecode --type 17 2>/dev/null|todos >>".escapeshellarg("/$diag/system/meminfo.txt")); +// mask IP addresses in lsof.txt +maskIP("/$diag/system/lsof.txt"); + // create ethernet information information (suppress errors) foreach ($ports as $port) { run("ethtool ".escapeshellarg($port)." 2>/dev/null|todos >>".escapeshellarg("/$diag/system/ethtool.txt")); @@ -706,12 +709,6 @@ if (file_exists($wgquick)) { run("todos <$wgquick >".escapeshellarg($log)); } -// generate unraid-api.txt -if (file_exists("/usr/local/sbin/unraid-api")) { - $log = "/$diag/system/unraid-api.txt"; - run("unraid-api report | todos >".escapeshellarg($log)); -} - // generate testparm.txt $testparm = run("testparm -s 2>/dev/null"); if (!$all) diff --git a/emhttp/plugins/dynamix/unraidPatch.page b/emhttp/plugins/dynamix/unraidPatch.page deleted file mode 100644 index d0734c8209..0000000000 --- a/emhttp/plugins/dynamix/unraidPatch.page +++ /dev/null @@ -1,29 +0,0 @@ -Menu="About" -Title="Unraid Patch" ---- - - - -
_(Click **Install** to download and install the **Unraid Patch** plugin)_
- - - - -  -: - diff --git a/share/docker/tailscale_container_hook b/share/docker/tailscale_container_hook index cd990c6556..817e22eb10 100755 --- a/share/docker/tailscale_container_hook +++ b/share/docker/tailscale_container_hook @@ -36,8 +36,8 @@ if [ "$(id -u)" != "0" ]; then error_handler fi -if [ -f /usr/local/bin/tailscale ] || [ -f /usr/local/bin/tailscaled ]; then - echo "Official Tailscale Sidecar container detected!" +if [ "${OFFICIAL_TAILSCALE_SIDECAR}" = "true" ]; then + echo "Official Tailscale Sidecar container routine enabled!" echo OFFICIAL_TS_SIDECAR="true" apk update >/dev/null 2>&1 @@ -361,12 +361,15 @@ if [ ! -z "${TAILSCALE_SERVE_PORT}" ]; then if [ -z "${TAILSCALE_SERVE_PROTOCOL_PORT}" ]; then TAILSCALE_SERVE_PROTOCOL_PORT="=443" fi + if [ -z "${TAILSCALE_SERVE_TARGET}" ]; then + TAILSCALE_SERVE_TARGET="http://localhost" + fi if [ "${TAILSCALE_FUNNEL}" = "true" ]; then echo "Enabling Funnel! See https://tailscale.com/kb/1223/funnel" - eval tailscale funnel --bg --"${TAILSCALE_SERVE_PROTOCOL}"${TAILSCALE_SERVE_PROTOCOL_PORT}${TAILSCALE_SERVE_PATH} http://localhost:"${TAILSCALE_SERVE_PORT}${TAILSCALE_SERVE_LOCALPATH}" | grep -v "To disable the proxy" + eval tailscale funnel --bg --"${TAILSCALE_SERVE_PROTOCOL}"${TAILSCALE_SERVE_PROTOCOL_PORT}${TAILSCALE_SERVE_PATH} ${TAILSCALE_SERVE_TARGET}:"${TAILSCALE_SERVE_PORT}${TAILSCALE_SERVE_LOCALPATH}" | grep -v "To disable the proxy" else echo "Enabling Serve! See https://tailscale.com/kb/1312/serve" - eval tailscale serve --bg --"${TAILSCALE_SERVE_PROTOCOL}"${TAILSCALE_SERVE_PROTOCOL_PORT}${TAILSCALE_SERVE_PATH} http://localhost:"${TAILSCALE_SERVE_PORT}${TAILSCALE_SERVE_LOCALPATH}" | grep -v "To disable the proxy" + eval tailscale serve --bg --"${TAILSCALE_SERVE_PROTOCOL}"${TAILSCALE_SERVE_PROTOCOL_PORT}${TAILSCALE_SERVE_PATH} ${TAILSCALE_SERVE_TARGET}:"${TAILSCALE_SERVE_PORT}${TAILSCALE_SERVE_LOCALPATH}" | grep -v "To disable the proxy" fi if [ "${TAILSCALE_SERVE_PROTOCOL}" = "https" ]; then TS_DNSNAME="$(tailscale status --json | jq -r '.Self.DNSName' | sed 's/\.$//')"