Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions emhttp/languages/en_US/helptext.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2367,6 +2367,12 @@ Generally speaking, it is recommended to leave this setting to its default value
IMPORTANT NOTE: If adjusting port mappings, do not modify the settings for the Container port as only the Host port can be adjusted.
:end

:docker_fixed_mac_help:
Assigns the container's MAC address on the selected Docker network endpoint. Use a valid unicast MAC address; the first octet must be even, e.g. 02:42:9a:0d:7e:c0.

This avoids using the legacy container-level --mac-address option in Extra Parameters.
:end

:docker_container_network_help:
This allows your container to utilize the network configuration of another container. Select the appropriate container from the list.<br>This setup can be particularly beneficial if you wish to route your container's traffic through a VPN.
:end
Expand Down
36 changes: 28 additions & 8 deletions emhttp/plugins/dynamix.docker.manager/include/CreateDocker.php
Original file line number Diff line number Diff line change
Expand Up @@ -202,18 +202,17 @@ function cpu_pinning() {
if (preg_match('/^container:(.*)/', $Network)) {
$Net_Container = str_replace("container:", "", $Network);
} else {
preg_match("/--(net|network)=container:[^\s]+/", $ExtraParams, $NetworkParam);
if (!empty($NetworkParam[0])) {
$Net_Container = explode(':', $NetworkParam[0])[1];
$Net_Container = str_replace(['"', "'"], '', $Net_Container);
preg_match("/--(?:net|network)(?:=|\s+)(['\"]?)container:([^'\"\s]+)\\1/", $ExtraParams, $NetworkParam);
if (!empty($NetworkParam[2])) {
$Net_Container = $NetworkParam[2];
}
}
// check if the container still exists from which the network should be used, if it doesn't exist any more recreate container with network none and don't start it
if (!empty($Net_Container)) {
$Net_Container_ID = $DockerClient->getContainerID($Net_Container);
if (empty($Net_Container_ID)) {
$cmd = str_replace('/docker run -d ', '/docker create ', $cmd);
$cmd = preg_replace("/--(net|network)=(['\"]?)container:[^'\"]+\\2/", "--network=none ", $cmd);
$cmd = preg_replace("/--(?:net|network)(?:=|\s+)(['\"]?)container:[^'\"\s]+\\1/", "--network=none ", $cmd);
}
}
// force kill container if still running after time-out
Expand Down Expand Up @@ -736,6 +735,8 @@ function removeConfig(num) {

function prepareConfig(form) {
var types = [], values = [], targets = [], vcpu = [];
var myMAC = $(form).find('input[name="contMyMAC"]').val().trim().replaceAll('-', ':').toLowerCase();
$(form).find('input[name="contMyMAC"]').val(myMAC);
if ($('select[name="contNetwork"]').val()=='host') {
$(form).find('input[name="confType[]"]').each(function(){types.push($(this).val());});
$(form).find('input[name="confValue[]"]').each(function(){values.push($(this));});
Expand All @@ -744,6 +745,7 @@ function prepareConfig(form) {
}
$(form).find('input[id^="box"]').each(function(){if ($(this).prop('checked')) vcpu.push($('#'+$(this).prop('id').replace('box','cpu')).text());});
form.contCPUset.value = vcpu.join(',');
return true;
}

function makeName(type) {
Expand Down Expand Up @@ -893,7 +895,7 @@ function prepareCategory() {
?>

<div id="canvas">
<form markdown="1" method="POST" autocomplete="off" onsubmit="prepareConfig(this)">
<form markdown="1" method="POST" autocomplete="off" onsubmit="return prepareConfig(this)">
<input type="hidden" name="csrf_token" value="<?=$var['csrf_token']?>">
<input type="hidden" name="contCPUset" value="">
<?if ($xmlType=='edit'):?>
Expand Down Expand Up @@ -1111,6 +1113,14 @@ function prepareCategory() {

</div>

<div markdown="1" class="myMAC noshow">
_(Fixed MAC address)_ (_(optional)_):
: <input type="text" name="contMyMAC" pattern="([0-9A-Fa-f]{2}[:-]){5}[0-9A-Fa-f]{2}|[0-9A-Fa-f]{12}">

:docker_fixed_mac_help:

</div>

<div markdown="1" class="netCONT noshow">
_(Container Network)_:
: <select name="netCONT" id="netCONT">
Expand Down Expand Up @@ -1560,19 +1570,30 @@ function prepareCategory() {
<?endforeach;?>

function showSubnet(bridge) {
if (bridge.match(/^(bridge|host|none)$/i) !== null) {
if (bridge.match(/^(host|none)$/i) !== null) {
$('.myIP').hide();
$('input[name="contMyIP"]').val('');
$('.myMAC').hide();
$('input[name="contMyMAC"]').val('');
$('.netCONT').hide();
$('#netCONT').val('');
} else if (bridge.match(/^(bridge)$/i) !== null) {
$('.myIP').hide();
$('input[name="contMyIP"]').val('');
$('.myMAC').show();
$('.netCONT').hide();
$('#netCONT').val('');
} else if (bridge.match(/^(container)$/i) !== null) {
$('.netCONT').show();
$('#netCONT').val('<?php echo (isset($xml) && isset($xml['Network'][1])) ? $xml['Network'][1] : ''; ?>');
$('.myIP').hide();
$('input[name="contMyIP"]').val('');
$('.myMAC').hide();
$('input[name="contMyMAC"]').val('');
} else {
$('.myIP').show();
$('#myIP').html('<?=_('Subnet')?>: '+subnet[bridge]);
$('.myMAC').show();
$('.netCONT').hide();
$('#netCONT').val('');
}
Expand Down Expand Up @@ -1932,4 +1953,3 @@ function load_contOverview() {
}
</script>
<?END:?>

94 changes: 88 additions & 6 deletions emhttp/plugins/dynamix.docker.manager/include/Helpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,66 @@ function xml_decode($string) {
return strval(html_entity_decode($string, ENT_XML1, 'UTF-8'));
}

function extraParamsWithQuotedValuesMasked($extraParams) {
return preg_replace('/"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"|\'[^\']*\'/', '""', $extraParams);
}

function replaceUnquotedExtraParams($extraParams, $callback) {
$parts = preg_split('/("[^"\\\\]*(?:\\\\.[^"\\\\]*)*"|\'[^\']*\')/', $extraParams, -1, PREG_SPLIT_DELIM_CAPTURE);
if ($parts === false) {
return $extraParams;
}
foreach ($parts as $i => $part) {
if ($part === '' || $part[0] === '"' || $part[0] === "'") {
continue;
}
$parts[$i] = $callback($part);
}
return implode('', $parts);
}

function extractMacAddressParam($extraParams) {
if (!is_string($extraParams)) {
return '';
}
$extraParams = extraParamsWithQuotedValuesMasked($extraParams);
if (preg_match('/(?:^|\s)--mac-address=([^\s\'"]+)/', $extraParams, $match)) {
return trim($match[1]);
}
if (preg_match('/(?:^|\s)--mac-address\s+([^\s\'"]+)/', $extraParams, $match)) {
return trim($match[1]);
}
return '';
}

function removeMacAddressParam($extraParams) {
if (!is_string($extraParams) || $extraParams === '') {
return '';
}
$extraParams = replaceUnquotedExtraParams($extraParams, function($part) {
$part = preg_replace('/(^|\s)--mac-address=[^\s\'"]+/', '$1', $part);
return preg_replace('/(^|\s)--mac-address\s+[^\s\'"]+/', '$1', $part);
});
return trim($extraParams);
}

function hasNetworkParam($extraParams) {
return is_string($extraParams) && preg_match('/(?:^|\s)--net(?:work)?(?:=|\s+)[^\s\'"]+/', extraParamsWithQuotedValuesMasked($extraParams));
}

function normalizeMacAddress($mac) {
$mac = strtolower(trim($mac ?? ''));
if ($mac === '') {
return '';
}
if (preg_match('/^[0-9a-f]{12}$/', $mac)) {
$mac = implode(':', str_split($mac, 2));
} else {
$mac = str_replace('-', ':', $mac);
}
return preg_match('/^([0-9a-f]{2}:){5}[0-9a-f]{2}$/', $mac) ? $mac : '';
}

function generateTSwebui($url, $serve, $webUI) {
if (!isset($webUI)) {
return '';
Expand Down Expand Up @@ -75,6 +135,9 @@ function postToXML($post, $setOwnership=false) {
$xml->Network = xml_encode($post['contNetwork']);
}
$xml->MyIP = xml_encode($post['contMyIP']);
$extraNetwork = hasNetworkParam($post['contExtraParams'] ?? '');
$myMAC = $extraNetwork ? '' : normalizeMacAddress(trim($post['contMyMAC'] ?? '') ?: extractMacAddressParam($post['contExtraParams'] ?? ''));
$xml->MyMAC = xml_encode($myMAC);
$xml->Shell = xml_encode($post['contShell']);
$xml->Privileged = strtolower($post['contPrivileged']??'')=='on' ? 'true' : 'false';
$xml->Support = xml_encode($post['contSupport']);
Expand All @@ -85,7 +148,7 @@ function postToXML($post, $setOwnership=false) {
$xml->WebUI = xml_encode(trim($post['contWebUI']));
$xml->TemplateURL = xml_encode($post['contTemplateURL']);
$xml->Icon = xml_encode(trim($post['contIcon']));
$xml->ExtraParams = xml_encode($post['contExtraParams']);
$xml->ExtraParams = xml_encode($myMAC && !$extraNetwork ? removeMacAddressParam($post['contExtraParams']) : $post['contExtraParams']);
$xml->PostArgs = xml_encode($post['contPostArgs']);
$xml->CPUset = xml_encode($post['contCPUset']);
$xml->DateInstalled = xml_encode(time());
Expand Down Expand Up @@ -149,6 +212,9 @@ function xmlToVar($xml) {
$out['Registry'] = xml_decode($xml->Registry);
$out['Network'] = xml_decode($xml->Network);
$out['MyIP'] = xml_decode($xml->MyIP ?? '');
$extraParams = xml_decode($xml->ExtraParams ?? '');
$extraNetwork = hasNetworkParam($extraParams);
$out['MyMAC'] = $extraNetwork ? '' : normalizeMacAddress(xml_decode($xml->MyMAC ?? '') ?: extractMacAddressParam($extraParams));
$out['Shell'] = xml_decode($xml->Shell ?? 'sh');
$out['Privileged'] = xml_decode($xml->Privileged);
$out['Support'] = xml_decode($xml->Support);
Expand All @@ -159,7 +225,7 @@ function xmlToVar($xml) {
$out['WebUI'] = xml_decode($xml->WebUI);
$out['TemplateURL'] = xml_decode($xml->TemplateURL);
$out['Icon'] = xml_decode($xml->Icon);
$out['ExtraParams'] = xml_decode($xml->ExtraParams);
$out['ExtraParams'] = $extraParams;
$out['PostArgs'] = xml_decode($xml->PostArgs);
$out['CPUset'] = xml_decode($xml->CPUset);
$out['DonateText'] = xml_decode($xml->DonateText);
Expand Down Expand Up @@ -325,13 +391,29 @@ function xmlToCommand($xml, $create_paths=false) {
$xml = xmlToVar($xml);
$cmdName = strlen($xml['Name']) ? '--name='.escapeshellarg($xml['Name']) : '';
$cmdPrivileged = strtolower($xml['Privileged'])=='true' ? '--privileged=true' : '';
$extraNetwork = hasNetworkParam($xml['ExtraParams']);
$cmdMyIP = '';
if (preg_match('/^container:(.*)/', $xml['Network'])) {
$cmdNetwork = preg_match('/\-\-net(work)?=/',$xml['ExtraParams']) ? "" : '--net='.escapeshellarg($xml['Network']);
$cmdNetwork = $extraNetwork ? "" : '--net='.escapeshellarg($xml['Network']);
} else {
$cmdNetwork = preg_match('/\-\-net(work)?=/',$xml['ExtraParams']) ? "" : '--net='.escapeshellarg(strtolower($xml['Network']));
$networkName = strtolower($xml['Network']);
if ($extraNetwork) {
$cmdNetwork = "";
} elseif (strlen($xml['MyMAC']) && !in_array($networkName, ['host','none'])) {
$xml['ExtraParams'] = removeMacAddressParam($xml['ExtraParams']);
$networkEndpoint = ['name='.$networkName];
foreach (explode(' ',str_replace(',',' ',$xml['MyIP'])) as $myIP) {
if ($myIP) $networkEndpoint[] = (strpos($myIP,':') !== false ? 'ip6=' : 'ip=').$myIP;
}
$networkEndpoint[] = 'mac-address='.$xml['MyMAC'];
$cmdNetwork = '--network='.escapeshellarg(implode(',', $networkEndpoint));
} else {
$cmdNetwork = '--net='.escapeshellarg($networkName);
}
}
if (!strlen($xml['MyMAC']) || preg_match('/^container:(.*)/', $xml['Network']) || $extraNetwork) {
foreach (explode(' ',str_replace(',',' ',$xml['MyIP'])) as $myIP) if ($myIP) $cmdMyIP .= (strpos($myIP,':') !== false ? '--ip6=' : '--ip=').escapeshellarg($myIP).' ';
}
$cmdMyIP = '';
foreach (explode(' ',str_replace(',',' ',$xml['MyIP'])) as $myIP) if ($myIP) $cmdMyIP .= (strpos($myIP,':')?'--ip6=':'--ip=').escapeshellarg($myIP).' ';
$cmdCPUset = strlen($xml['CPUset']) ? '--cpuset-cpus='.escapeshellarg($xml['CPUset']) : '';
$Volumes = [''];
$Ports = [''];
Expand Down
Loading
Loading