[Web] Allow CIDR as allowed API networks; other minor fixes
parent
066b23bb02
commit
aef15f004a
|
@ -113,28 +113,25 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
|
|||
</form>
|
||||
</div>
|
||||
|
||||
<legend style="margin-top:20px">
|
||||
<span style="font-size:12px" class="arrow rotate glyphicon glyphicon-wrench"></span> API
|
||||
<legend style="cursor:pointer;" data-target="#admin_api" class="arrow-toggle" unselectable="on" data-toggle="collapse">
|
||||
<span style="font-size:12px" class="arrow rotate glyphicon glyphicon-menu-down"></span> API
|
||||
</legend>
|
||||
<div id="admin_api" class="collapse">
|
||||
<?php
|
||||
$api_ro = admin_api('ro', 'get');
|
||||
$api_rw = admin_api('rw', 'get');
|
||||
?>
|
||||
<div class="panel-group" id="accordion">
|
||||
<div class="col-lg-6">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title">
|
||||
<a data-toggle="collapse" data-parent="#accordion" href="#api-ro">
|
||||
⇇ Read-Only Access</a>
|
||||
</h4>
|
||||
<h4 class="panel-title">⇇ Read-Only Access</h4>
|
||||
</div>
|
||||
<div id="api-ro" class="panel-collapse collapse">
|
||||
<div class="panel-body">
|
||||
<form class="form-horizontal" autocapitalize="none" autocorrect="off" role="form" method="post">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-3" for="allow_from_ro"><?=$lang['admin']['api_allow_from'];?>:</label>
|
||||
<div class="col-sm-9">
|
||||
<textarea class="form-control" rows="2" name="allow_from" id="allow_from_ro" <?=($api_ro['skip_ip_check'] == 1) ? 'disabled' : null;?> required><?=htmlspecialchars($api_ro['allow_from']);?></textarea>
|
||||
<textarea class="form-control textarea-code" rows="7" name="allow_from" id="allow_from_ro" <?=($api_ro['skip_ip_check'] == 1) ? 'disabled' : null;?> required><?=htmlspecialchars($api_ro['allow_from']);?></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
|
@ -161,8 +158,8 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
|
|||
<div class="col-sm-offset-3 col-sm-9">
|
||||
<p class="help-block"><?=$lang['admin']['api_info'];?></p>
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-sm btn-default" name="admin_api[ro]" type="submit" href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
|
||||
<button class="btn btn-sm btn-primary" name="admin_api_regen_key[ro]" type="submit" href="#"><?=$lang['admin']['regen_api_key'];?></button>
|
||||
<button class="btn btn-sm btn-success" name="admin_api[ro]" type="submit" href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
|
||||
<button class="btn btn-sm btn-default admin-ays-dialog" name="admin_api_regen_key[ro]" type="submit" href="#"><?=$lang['admin']['regen_api_key'];?></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -170,20 +167,17 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title">
|
||||
<a data-toggle="collapse" data-parent="#accordion" href="#api-rw">
|
||||
⇄ Read-Write Access</a>
|
||||
</h4>
|
||||
<h4 class="panel-title">⇄ Read-Write Access</h4>
|
||||
</div>
|
||||
<div id="api-rw" class="panel-collapse collapse">
|
||||
<div class="panel-body">
|
||||
<form class="form-horizontal" autocapitalize="none" autocorrect="off" role="form" method="post">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-3" for="allow_from_rw"><?=$lang['admin']['api_allow_from'];?>:</label>
|
||||
<div class="col-sm-9">
|
||||
<textarea class="form-control" rows="2" name="allow_from" id="allow_from_rw" <?=($api_rw['skip_ip_check'] == 1) ? 'disabled' : null;?> required><?=htmlspecialchars($api_rw['allow_from']);?></textarea>
|
||||
<textarea class="form-control textarea-code" rows="7" name="allow_from" id="allow_from_rw" <?=($api_rw['skip_ip_check'] == 1) ? 'disabled' : null;?> required><?=htmlspecialchars($api_rw['allow_from']);?></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
|
@ -210,8 +204,8 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
|
|||
<div class="col-sm-offset-3 col-sm-9">
|
||||
<p class="help-block"><?=$lang['admin']['api_info'];?></p>
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-sm btn-default" name="admin_api[rw]" type="submit" href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
|
||||
<button class="btn btn-sm btn-primary" name="admin_api_regen_key[rw]" type="submit" href="#"><?=$lang['admin']['regen_api_key'];?></button>
|
||||
<button class="btn btn-sm btn-success" name="admin_api[rw]" type="submit" href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
|
||||
<button class="btn btn-sm btn-default admin-ays-dialog" name="admin_api_regen_key[rw]" type="submit" href="#"><?=$lang['admin']['regen_api_key'];?></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -220,7 +214,6 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
<?php
|
||||
function valid_network($network) {
|
||||
$cidr = explode('/', $network);
|
||||
if (filter_var($cidr[0], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) && (!isset($cidr[1]) || ($cidr[1] >= 0 && $cidr[1] <= 32))) {
|
||||
return true;
|
||||
}
|
||||
elseif (filter_var($cidr[0], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) && (!isset($cidr[1]) || ($cidr[1] >= 0 && $cidr[1] <= 128))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function valid_hostname($hostname) {
|
||||
return filter_var($hostname, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME);
|
||||
}
|
||||
|
||||
function fail2ban($_action, $_data = null) {
|
||||
global $redis;
|
||||
global $lang;
|
||||
|
@ -196,6 +181,14 @@ function fail2ban($_action, $_data = null) {
|
|||
if (valid_network($wl_item) || valid_hostname($wl_item)) {
|
||||
$redis->hSet('F2B_WHITELIST', $wl_item, 1);
|
||||
}
|
||||
else {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('network_host_invalid', $wl_item)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -206,6 +199,14 @@ function fail2ban($_action, $_data = null) {
|
|||
if (valid_network($bl_item) || valid_hostname($bl_item)) {
|
||||
$redis->hSet('F2B_BLACKLIST', $bl_item, 1);
|
||||
}
|
||||
else {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('network_host_invalid', $bl_item)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,76 @@ function isset_has_content($var) {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
// Validates ips and cidrs
|
||||
function valid_network($network) {
|
||||
if (filter_var($network, FILTER_VALIDATE_IP)) {
|
||||
return true;
|
||||
}
|
||||
$parts = explode('/', $network);
|
||||
if (count($parts) != 2) {
|
||||
return false;
|
||||
}
|
||||
$ip = $parts[0];
|
||||
$netmask = $parts[1];
|
||||
if (!preg_match("/^\d+$/", $netmask)){
|
||||
return false;
|
||||
}
|
||||
$netmask = intval($parts[1]);
|
||||
if ($netmask < 0) {
|
||||
return false;
|
||||
}
|
||||
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
|
||||
return $netmask <= 32;
|
||||
}
|
||||
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
|
||||
return $netmask <= 128;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function valid_hostname($hostname) {
|
||||
return filter_var($hostname, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME);
|
||||
}
|
||||
// Thanks to https://stackoverflow.com/a/49373789
|
||||
// Validates exact ip matches and ip-in-cidr, ipv4 and ipv6
|
||||
function ip_acl($ip, $networks) {
|
||||
foreach($networks as $network) {
|
||||
if (filter_var($network, FILTER_VALIDATE_IP)) {
|
||||
if ($ip == $network) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$ipb = inet_pton($ip);
|
||||
$iplen = strlen($ipb);
|
||||
if (strlen($ipb) < 4) {
|
||||
continue;
|
||||
}
|
||||
$ar = explode('/', $network);
|
||||
$ip1 = $ar[0];
|
||||
$ip1b = inet_pton($ip1);
|
||||
$ip1len = strlen($ip1b);
|
||||
if ($ip1len != $iplen) {
|
||||
continue;
|
||||
}
|
||||
if (count($ar)>1) {
|
||||
$bits=(int)($ar[1]);
|
||||
}
|
||||
else {
|
||||
$bits = $iplen * 8;
|
||||
}
|
||||
for ($c=0; $bits>0; $c++) {
|
||||
$bytemask = ($bits < 8) ? 0xff ^ ((1 << (8-$bits))-1) : 0xff;
|
||||
if (((ord($ipb[$c]) ^ ord($ip1b[$c])) & $bytemask) != 0) {
|
||||
continue 2;
|
||||
}
|
||||
$bits-=8;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function hash_password($password) {
|
||||
$salt_str = bin2hex(openssl_random_pseudo_bytes(8));
|
||||
return "{SSHA256}".base64_encode(hash('sha256', $password . $salt_str, true) . $salt_str);
|
||||
|
@ -1160,18 +1230,24 @@ function admin_api($access, $action, $data = null) {
|
|||
);
|
||||
return false;
|
||||
}
|
||||
switch ($access) {
|
||||
case "rw":
|
||||
switch ($action) {
|
||||
case "edit":
|
||||
$active = (isset($data['active'])) ? 1 : 0;
|
||||
if ($access !== "ro" && $access !== "rw") {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__),
|
||||
'msg' => 'invalid access type'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if ($action == "edit") {
|
||||
$active = (!empty($data['active'])) ? 1 : 0;
|
||||
$skip_ip_check = (isset($data['skip_ip_check'])) ? 1 : 0;
|
||||
$allow_from = array_map('trim', preg_split( "/( |,|;|\n)/", $data['allow_from']));
|
||||
foreach ($allow_from as $key => $val) {
|
||||
if (empty($val)) {
|
||||
unset($allow_from[$key]);
|
||||
continue;
|
||||
}
|
||||
if (!filter_var($val, FILTER_VALIDATE_IP)) {
|
||||
if (valid_network($val) !== true) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'warning',
|
||||
'log' => array(__FUNCTION__, $data),
|
||||
|
@ -1197,37 +1273,45 @@ function admin_api($access, $action, $data = null) {
|
|||
strtoupper(bin2hex(random_bytes(3))),
|
||||
strtoupper(bin2hex(random_bytes(3)))
|
||||
));
|
||||
$stmt = $pdo->query("SELECT `api_key` FROM `api` WHERE `access` = 'rw'");
|
||||
$stmt = $pdo->query("SELECT `api_key` FROM `api` WHERE `access` = '" . $access . "'");
|
||||
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
||||
if (empty($num_results)) {
|
||||
$stmt = $pdo->prepare("INSERT INTO `api` (`api_key`, `skip_ip_check`, `active`, `allow_from`, `access`)
|
||||
VALUES (:api_key, :skip_ip_check, :active, :allow_from, 'rw');");
|
||||
VALUES (:api_key, :skip_ip_check, :active, :allow_from, :access);");
|
||||
$stmt->execute(array(
|
||||
':api_key' => $api_key,
|
||||
':skip_ip_check' => $skip_ip_check,
|
||||
':active' => $active,
|
||||
':allow_from' => $allow_from
|
||||
':allow_from' => $allow_from,
|
||||
':access' => $access
|
||||
));
|
||||
}
|
||||
else {
|
||||
if ($skip_ip_check == 0) {
|
||||
$stmt = $pdo->prepare("UPDATE `api` SET `skip_ip_check` = :skip_ip_check, `active` = :active, `allow_from` = :allow_from WHERE `access` = 'rw';");
|
||||
$stmt = $pdo->prepare("UPDATE `api` SET `skip_ip_check` = :skip_ip_check,
|
||||
`active` = :active,
|
||||
`allow_from` = :allow_from
|
||||
WHERE `access` = :access;");
|
||||
$stmt->execute(array(
|
||||
':active' => $active,
|
||||
':skip_ip_check' => $skip_ip_check,
|
||||
':allow_from' => $allow_from
|
||||
':allow_from' => $allow_from,
|
||||
':access' => $access
|
||||
));
|
||||
}
|
||||
else {
|
||||
$stmt = $pdo->prepare("UPDATE `api` SET `skip_ip_check` = :skip_ip_check, `active` = :active WHERE `access` = 'rw';");
|
||||
$stmt = $pdo->prepare("UPDATE `api` SET `skip_ip_check` = :skip_ip_check,
|
||||
`active` = :active
|
||||
WHERE `access` = :access;");
|
||||
$stmt->execute(array(
|
||||
':active' => $active,
|
||||
':skip_ip_check' => $skip_ip_check
|
||||
':skip_ip_check' => $skip_ip_check,
|
||||
':access' => $access
|
||||
));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "regen_key":
|
||||
}
|
||||
elseif ($action == "regen_key") {
|
||||
$api_key = implode('-', array(
|
||||
strtoupper(bin2hex(random_bytes(3))),
|
||||
strtoupper(bin2hex(random_bytes(3))),
|
||||
|
@ -1235,103 +1319,17 @@ function admin_api($access, $action, $data = null) {
|
|||
strtoupper(bin2hex(random_bytes(3))),
|
||||
strtoupper(bin2hex(random_bytes(3)))
|
||||
));
|
||||
$stmt = $pdo->prepare("UPDATE `api` SET `api_key` = :api_key WHERE `access` = 'rw'");
|
||||
$stmt->execute(array(
|
||||
':api_key' => $api_key
|
||||
));
|
||||
break;
|
||||
case "get":
|
||||
$stmt = $pdo->query("SELECT * FROM `api` WHERE `access` = 'rw'");
|
||||
$apidata = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
return $apidata;
|
||||
break;
|
||||
}
|
||||
case "ro":
|
||||
switch ($action) {
|
||||
case "edit":
|
||||
$active = (isset($data['active'])) ? 1 : 0;
|
||||
$skip_ip_check = (isset($data['skip_ip_check'])) ? 1 : 0;
|
||||
$allow_from = array_map('trim', preg_split( "/( |,|;|\n)/", $data['allow_from']));
|
||||
foreach ($allow_from as $key => $val) {
|
||||
if (empty($val)) {
|
||||
continue;
|
||||
}
|
||||
if (!filter_var($val, FILTER_VALIDATE_IP)) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'warning',
|
||||
'log' => array(__FUNCTION__, $data),
|
||||
'msg' => array('ip_invalid', htmlspecialchars($allow_from[$key]))
|
||||
);
|
||||
unset($allow_from[$key]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$allow_from = implode(',', array_unique(array_filter($allow_from)));
|
||||
if (empty($allow_from) && $skip_ip_check == 0) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $data),
|
||||
'msg' => 'ip_list_empty'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
$api_key = implode('-', array(
|
||||
strtoupper(bin2hex(random_bytes(3))),
|
||||
strtoupper(bin2hex(random_bytes(3))),
|
||||
strtoupper(bin2hex(random_bytes(3))),
|
||||
strtoupper(bin2hex(random_bytes(3))),
|
||||
strtoupper(bin2hex(random_bytes(3)))
|
||||
));
|
||||
$stmt = $pdo->query("SELECT `api_key` FROM `api` WHERE `access` = 'ro'");
|
||||
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
||||
if (empty($num_results)) {
|
||||
$stmt = $pdo->prepare("INSERT INTO `api` (`api_key`, `skip_ip_check`, `active`, `allow_from`, `access`)
|
||||
VALUES (:api_key, :skip_ip_check, :active, :allow_from, 'ro');");
|
||||
$stmt = $pdo->prepare("UPDATE `api` SET `api_key` = :api_key WHERE `access` = :access");
|
||||
$stmt->execute(array(
|
||||
':api_key' => $api_key,
|
||||
':skip_ip_check' => $skip_ip_check,
|
||||
':active' => $active,
|
||||
':allow_from' => $allow_from
|
||||
':access' => $access
|
||||
));
|
||||
}
|
||||
else {
|
||||
if ($skip_ip_check == 0) {
|
||||
$stmt = $pdo->prepare("UPDATE `api` SET `skip_ip_check` = :skip_ip_check, `active` = :active, `allow_from` = :allow_from WHERE `access` = 'ro';");
|
||||
$stmt->execute(array(
|
||||
':active' => $active,
|
||||
':skip_ip_check' => $skip_ip_check,
|
||||
':allow_from' => $allow_from
|
||||
));
|
||||
}
|
||||
else {
|
||||
$stmt = $pdo->prepare("UPDATE `api` SET `skip_ip_check` = :skip_ip_check, `active` = :active WHERE `access` = 'ro';");
|
||||
$stmt->execute(array(
|
||||
':active' => $active,
|
||||
':skip_ip_check' => $skip_ip_check
|
||||
));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "regen_key":
|
||||
$api_key = implode('-', array(
|
||||
strtoupper(bin2hex(random_bytes(3))),
|
||||
strtoupper(bin2hex(random_bytes(3))),
|
||||
strtoupper(bin2hex(random_bytes(3))),
|
||||
strtoupper(bin2hex(random_bytes(3))),
|
||||
strtoupper(bin2hex(random_bytes(3)))
|
||||
));
|
||||
$stmt = $pdo->prepare("UPDATE `api` SET `api_key` = :api_key WHERE `access` = 'ro'");
|
||||
$stmt->execute(array(
|
||||
':api_key' => $api_key
|
||||
));
|
||||
break;
|
||||
case "get":
|
||||
$stmt = $pdo->query("SELECT * FROM `api` WHERE `access` = 'ro'");
|
||||
elseif ($action == "get") {
|
||||
$stmt = $pdo->query("SELECT * FROM `api` WHERE `access` = '" . $access . "'");
|
||||
$apidata = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
$apidata['allow_from'] = str_replace(',', PHP_EOL, $apidata['allow_from']);
|
||||
return $apidata;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'success',
|
||||
|
|
|
@ -179,6 +179,8 @@ function get_remote_ip($anonymize = null) {
|
|||
}
|
||||
}
|
||||
|
||||
// Load core functions first
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.inc.php';
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/sessions.inc.php';
|
||||
|
||||
// IMAP lib
|
||||
|
@ -215,7 +217,6 @@ if(file_exists($langFile)) {
|
|||
$lang = array_merge_real($lang, json_decode(file_get_contents($langFile), true));
|
||||
}
|
||||
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.inc.php';
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.acl.inc.php';
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.app_passwd.inc.php';
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.mailbox.inc.php';
|
||||
|
|
|
@ -53,7 +53,7 @@ if (!empty($_SERVER['HTTP_X_API_KEY'])) {
|
|||
$skip_ip_check = ($api_return['skip_ip_check'] == 1);
|
||||
$remote = get_remote_ip(false);
|
||||
$allow_from = array_map('trim', preg_split( "/( |,|;|\n)/", $api_return['allow_from']));
|
||||
if (in_array($remote, $allow_from) || $skip_ip_check === true) {
|
||||
if ($skip_ip_check === true || ip_acl($remote, $allow_from)) {
|
||||
$_SESSION['mailcow_cc_username'] = 'API';
|
||||
$_SESSION['mailcow_cc_role'] = 'admin';
|
||||
$_SESSION['mailcow_cc_api'] = true;
|
||||
|
|
|
@ -21,6 +21,7 @@ jQuery(function($){
|
|||
$("#mass_exclude").change(function(){ $("#mass_include").selectpicker('deselectAll'); });
|
||||
$("#mass_include").change(function(){ $("#mass_exclude").selectpicker('deselectAll'); });
|
||||
$("#mass_disarm").click(function() { $("#mass_send").attr("disabled", !this.checked); });
|
||||
$(".admin-ays-dialog").click(function() { return confirm(lang.ays); });
|
||||
$(".validate_rspamd_regex").click(function( event ) {
|
||||
event.preventDefault();
|
||||
var regex_map_id = $(this).data('regex-map');
|
||||
|
|
|
@ -122,7 +122,7 @@
|
|||
"admin_details": "Administrator bearbeiten",
|
||||
"admin_domains": "Domain-Zuweisungen",
|
||||
"advanced_settings": "Erweiterte Einstellungen",
|
||||
"api_allow_from": "IP-Adressen für Zugriff",
|
||||
"api_allow_from": "IP-Adressen oder Netzwerke (CIDR Notation) für Zugriff auf API",
|
||||
"api_info": "Das API befindet sich noch in Entwicklung, die Dokumentation kann unter <a href=\"/api\">/api</a> abgerufen werden.",
|
||||
"api_key": "API-Key",
|
||||
"api_skip_ip_check": "IP-Check für API nicht ausführen",
|
||||
|
@ -131,6 +131,7 @@
|
|||
"apps_name": "\"mailcow Apps\" Name",
|
||||
"arrival_time": "Ankunftszeit (Serverzeit)",
|
||||
"authed_user": "Auth. Benutzer",
|
||||
"ays": "Soll der Vorgang wirklich ausgeführt werden?",
|
||||
"ban_list_info": "Übersicht ausgesperrter Netzwerke: <b>Netzwerk (verbleibende Banzeit) - [Aktionen]</b>.<br />IPs, die zum Entsperren eingereiht werden, verlassen die Liste aktiver Bans nach wenigen Sekunden.<br />Rote Labels sind Indikatoren für aktive Blacklisteinträge.",
|
||||
"change_logo": "Logo ändern",
|
||||
"configuration": "Konfiguration",
|
||||
|
|
|
@ -122,7 +122,7 @@
|
|||
"admin_details": "Edit administrator details",
|
||||
"admin_domains": "Domain assignments",
|
||||
"advanced_settings": "Advanced settings",
|
||||
"api_allow_from": "Allow API access from these IPs (separated by comma or new line)",
|
||||
"api_allow_from": "Allow API access from these IPs/CIDR network notations",
|
||||
"api_info": "The API is a work in progress. The documentation can be found at <a href=\"/api\">/api</a>",
|
||||
"api_key": "API key",
|
||||
"api_skip_ip_check": "Skip IP check for API",
|
||||
|
@ -131,6 +131,7 @@
|
|||
"apps_name": "\"mailcow Apps\" name",
|
||||
"arrival_time": "Arrival time (server time)",
|
||||
"authed_user": "Auth. user",
|
||||
"ays": "Are you sure you want to proceed?",
|
||||
"ban_list_info": "See a list of banned IPs below: <b>network (remaining ban time) - [actions]</b>.<br />IPs queued to be unbanned will be removed from the active ban list within a few seconds.<br />Red labels indicate active permanent bans by blacklisting.",
|
||||
"change_logo": "Change logo",
|
||||
"configuration": "Configuration",
|
||||
|
|
Loading…
Reference in New Issue