[Postfix] Finally here: MX based transport map routing; Sorry it took years, Patrik
[Web] Small fixesmaster
parent
f54ab6f867
commit
8a83587800
|
@ -337,6 +337,19 @@ query = SELECT goto FROM alias
|
|||
AND alias_domain.active='1'
|
||||
EOF
|
||||
|
||||
# MX based routing
|
||||
cat <<EOF > /opt/postfix/conf/sql/mysql_mbr_access_maps.cf
|
||||
# Autogenerated by mailcow
|
||||
user = ${DBUSER}
|
||||
password = ${DBPASS}
|
||||
hosts = unix:/var/run/mysqld/mysqld.sock
|
||||
dbname = ${DBNAME}
|
||||
query = SELECT CONCAT('FILTER smtp_via_transport_maps:', nexthop) as transport FROM transports
|
||||
WHERE '%s' REGEXP destination
|
||||
AND active='1'
|
||||
AND is_mx_based='1';
|
||||
EOF
|
||||
|
||||
# Reject sasl usernames with smtp disabled
|
||||
cat <<EOF > /opt/postfix/conf/sql/mysql_sasl_access_maps.cf
|
||||
# Autogenerated by mailcow
|
||||
|
|
|
@ -78,6 +78,7 @@ postscreen_non_smtp_command_enable = no
|
|||
postscreen_pipelining_enable = no
|
||||
proxy_read_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_sasl_passwd_maps_transport_maps.cf,
|
||||
proxy:mysql:/opt/postfix/conf/sql/mysql_sasl_access_maps.cf,
|
||||
proxy:mysql:/opt/postfix/conf/sql/mysql_mbr_access_maps.cf,
|
||||
proxy:mysql:/opt/postfix/conf/sql/mysql_tls_enforce_in_policy.cf,
|
||||
$sender_dependent_default_transport_maps,
|
||||
$smtp_tls_policy_maps,
|
||||
|
@ -116,6 +117,7 @@ smtpd_hard_error_limit = ${stress?1}${stress:5}
|
|||
smtpd_helo_required = yes
|
||||
smtpd_proxy_timeout = 600s
|
||||
smtpd_recipient_restrictions = check_sasl_access proxy:mysql:/opt/postfix/conf/sql/mysql_sasl_access_maps.cf,
|
||||
check_recipient_mx_access proxy:mysql:/opt/postfix/conf/sql/mysql_mbr_access_maps.cf,
|
||||
permit_sasl_authenticated,
|
||||
permit_mynetworks,
|
||||
check_recipient_access proxy:mysql:/opt/postfix/conf/sql/mysql_tls_enforce_in_policy.cf,
|
||||
|
|
|
@ -431,19 +431,19 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
|
|||
<legend><?=$lang['admin']['add_relayhost'];?></legend>
|
||||
<p class="help-block"><?=$lang['admin']['add_relayhost_hint'];?></p>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="col-md-8">
|
||||
<form class="form" data-id="rlyhost" role="form" method="post">
|
||||
<div class="form-group">
|
||||
<label for="rlyhost_hostname"><?=$lang['admin']['host'];?></label>
|
||||
<input class="form-control input-sm" id="rlyhost_hostname" name="hostname" placeholder='[0.0.0.0], [0.0.0.0]:25, host:25, host, [host]:25' required>
|
||||
<input class="form-control" id="rlyhost_hostname" name="hostname" placeholder='[0.0.0.0], [0.0.0.0]:25, host:25, host, [host]:25' required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="rlyhost_username"><?=$lang['admin']['username'];?></label>
|
||||
<input class="form-control input-sm" id="rlyhost_username" name="username">
|
||||
<input class="form-control" id="rlyhost_username" name="username">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="rlyhost_password"><?=$lang['admin']['password'];?></label>
|
||||
<input class="form-control input-sm" id="rlyhost_password" name="password">
|
||||
<input class="form-control" id="rlyhost_password" name="password">
|
||||
</div>
|
||||
<button class="btn btn-default" data-action="add_item" data-id="rlyhost" data-api-url='add/relayhost' data-api-attr='{}' href="#"><i class="bi bi-plus-lg"></i> <?=$lang['admin']['add'];?></button>
|
||||
</form>
|
||||
|
@ -474,29 +474,29 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
|
|||
<legend><?=$lang['admin']['add_transport'];?></legend>
|
||||
<p class="help-block"><?=$lang['admin']['add_transports_hint'];?></p>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="col-md-8">
|
||||
<form class="form" data-id="transport" role="form" method="post">
|
||||
<div class="form-group">
|
||||
<label for="transport_destination"><?=$lang['admin']['destination'];?></label>
|
||||
<input class="form-control input-sm" id="transport_destination" name="destination" placeholder='<?=$lang['admin']['transport_dest_format'];?>' required>
|
||||
<input class="form-control" id="transport_destination" name="destination" placeholder='<?=$lang['admin']['transport_dest_format'];?>' required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="transport_nexthop"><?=$lang['admin']['nexthop'];?></label>
|
||||
<input class="form-control input-sm" id="transport_nexthop" name="nexthop" placeholder='host:25, host, [host]:25, [0.0.0.0]:25' required>
|
||||
<input class="form-control" id="transport_nexthop" name="nexthop" placeholder='host:25, host, [host]:25, [0.0.0.0]:25' required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="transport_username"><?=$lang['admin']['username'];?></label>
|
||||
<input class="form-control input-sm" id="transport_username" name="username">
|
||||
<input class="form-control" id="transport_username" name="username">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="transport_password"><?=$lang['admin']['password'];?></label>
|
||||
<input class="form-control input-sm" id="transport_password" name="password">
|
||||
<input class="form-control" id="transport_password" name="password">
|
||||
</div>
|
||||
<!-- <div class="form-group">
|
||||
<div class="form-group">
|
||||
<label>
|
||||
<input type="checkbox" name="lookup_mx" value="1"> <?=$lang['admin']['lookup_mx'];?>
|
||||
<input type="checkbox" name="is_mx_based" value="1"> <?=$lang['admin']['lookup_mx'];?>
|
||||
</label>
|
||||
</div> -->
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>
|
||||
<input type="checkbox" name="active" value="1"> <?=$lang['admin']['active'];?>
|
||||
|
|
|
@ -241,3 +241,6 @@ table.footable>tbody>tr.footable-empty>td {
|
|||
legend > [class^="bi-"]::before, legend > [class*=" bi-"]::before {
|
||||
vertical-align: 0em !important;
|
||||
}
|
||||
code {
|
||||
font-size: inherit;
|
||||
}
|
|
@ -1095,6 +1095,7 @@ if (isset($_SESSION['mailcow_cc_role'])) {
|
|||
<h4><?=$lang['edit']['resource'];?></h4>
|
||||
<form class="form-horizontal" role="form" method="post" data-id="edittransport">
|
||||
<input type="hidden" value="0" name="active">
|
||||
<input type="hidden" value="0" name="is_mx_based">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="destination"><?=$lang['add']['destination'];?></label>
|
||||
<div class="col-sm-10">
|
||||
|
@ -1119,6 +1120,13 @@ if (isset($_SESSION['mailcow_cc_role'])) {
|
|||
<input type="text" data-hibp="true" class="form-control" name="password" value="<?=htmlspecialchars($result['password'], ENT_QUOTES, 'UTF-8');?>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<div class="checkbox">
|
||||
<label><input type="checkbox" value="1" name="is_mx_based" <?=($result['is_mx_based']=="1") ? "checked" : null;?>> <?=$lang['edit']['lookup_mx'];?></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<div class="checkbox">
|
||||
|
|
|
@ -192,7 +192,7 @@ function transport($_action, $_data = null) {
|
|||
}
|
||||
$destinations = array_map('trim', preg_split( "/( |,|;|\n)/", $_data['destination']));
|
||||
$active = intval($_data['active']);
|
||||
$lookup_mx = intval($_data['lookup_mx']);
|
||||
$is_mx_based = intval($_data['is_mx_based']);
|
||||
$nexthop = trim($_data['nexthop']);
|
||||
if (filter_var($nexthop, FILTER_VALIDATE_IP)) {
|
||||
$nexthop = '[' . $nexthop . ']';
|
||||
|
@ -238,7 +238,16 @@ function transport($_action, $_data = null) {
|
|||
continue;
|
||||
}
|
||||
// ".domain" is a valid destination, "..domain" is not
|
||||
if (empty($dest) || (is_valid_domain_name(preg_replace('/^' . preg_quote('.', '/') . '/', '', $dest)) === false && $dest != '*' && filter_var($dest, FILTER_VALIDATE_EMAIL) === false)) {
|
||||
if ($is_mx_based == 0 && (empty($dest) || (is_valid_domain_name(preg_replace('/^' . preg_quote('.', '/') . '/', '', $dest)) === false && $dest != '*' && filter_var($dest, FILTER_VALIDATE_EMAIL) === false))) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('invalid_destination', $dest)
|
||||
);
|
||||
unset($destinations[$d_ix]);
|
||||
continue;
|
||||
}
|
||||
if ($is_mx_based == 1 && (empty($dest) || @preg_match('/' . $dest . '/', null) === false)) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
|
@ -275,14 +284,14 @@ function transport($_action, $_data = null) {
|
|||
}
|
||||
}
|
||||
foreach ($destinations as $insert_dest) {
|
||||
$stmt = $pdo->prepare("INSERT INTO `transports` (`nexthop`, `destination`, `username` , `password`, `lookup_mx`, `active`)
|
||||
VALUES (:nexthop, :destination, :username, :password, :lookup_mx, :active)");
|
||||
$stmt = $pdo->prepare("INSERT INTO `transports` (`nexthop`, `destination`, `is_mx_based`, `username` , `password`, `active`)
|
||||
VALUES (:nexthop, :destination, :is_mx_based, :username, :password, :active)");
|
||||
$stmt->execute(array(
|
||||
':nexthop' => $nexthop,
|
||||
':destination' => $insert_dest,
|
||||
':is_mx_based' => $is_mx_based,
|
||||
':username' => $username,
|
||||
':password' => str_replace(':', '\:', $password),
|
||||
':lookup_mx' => $lookup_mx,
|
||||
':active' => $active
|
||||
));
|
||||
}
|
||||
|
@ -318,7 +327,7 @@ function transport($_action, $_data = null) {
|
|||
$nexthop = (!empty($_data['nexthop'])) ? trim($_data['nexthop']) : $is_now['nexthop'];
|
||||
$username = (isset($_data['username'])) ? trim($_data['username']) : $is_now['username'];
|
||||
$password = (isset($_data['password'])) ? trim($_data['password']) : $is_now['password'];
|
||||
$lookup_mx = (isset($_data['lookup_mx']) && $_data['lookup_mx'] != '') ? intval($_data['lookup_mx']) : $is_now['lookup_mx'];
|
||||
$is_mx_based = (isset($_data['is_mx_based']) && $_data['is_mx_based'] != '') ? intval($_data['is_mx_based']) : $is_now['is_mx_based'];
|
||||
$active = (isset($_data['active']) && $_data['active'] != '') ? intval($_data['active']) : $is_now['active'];
|
||||
}
|
||||
else {
|
||||
|
@ -353,6 +362,22 @@ function transport($_action, $_data = null) {
|
|||
}
|
||||
}
|
||||
}
|
||||
if ($is_mx_based == 0 && (empty($destination) || (is_valid_domain_name(preg_replace('/^' . preg_quote('.', '/') . '/', '', $destination)) === false && $destination != '*' && filter_var($destination, FILTER_VALIDATE_EMAIL) === false))) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('invalid_destination', $destination)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if ($is_mx_based == 1 && (empty($destination) || @preg_match('/' . $destination . '/', null) === false)) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('invalid_destination', $destination)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (isset($next_hop_matches[1])) {
|
||||
if (in_array($next_hop_clean, $existing_nh)) {
|
||||
$_SESSION['return'][] = array(
|
||||
|
@ -381,19 +406,19 @@ function transport($_action, $_data = null) {
|
|||
try {
|
||||
$stmt = $pdo->prepare("UPDATE `transports` SET
|
||||
`destination` = :destination,
|
||||
`is_mx_based` = :is_mx_based,
|
||||
`nexthop` = :nexthop,
|
||||
`username` = :username,
|
||||
`password` = :password,
|
||||
`lookup_mx` = :lookup_mx,
|
||||
`active` = :active
|
||||
WHERE `id` = :id");
|
||||
$stmt->execute(array(
|
||||
':id' => $id,
|
||||
':destination' => $destination,
|
||||
':is_mx_based' => $is_mx_based,
|
||||
':nexthop' => $nexthop,
|
||||
':username' => $username,
|
||||
':password' => $password,
|
||||
':lookup_mx' => $lookup_mx,
|
||||
':active' => $active
|
||||
));
|
||||
$stmt = $pdo->prepare("UPDATE `transports` SET
|
||||
|
@ -456,7 +481,7 @@ function transport($_action, $_data = null) {
|
|||
return false;
|
||||
}
|
||||
$transports = array();
|
||||
$stmt = $pdo->query("SELECT `id`, `destination`, `nexthop`, `username` FROM `transports`");
|
||||
$stmt = $pdo->query("SELECT `id`, `is_mx_based`, `destination`, `nexthop`, `username` FROM `transports`");
|
||||
$transports = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
return $transports;
|
||||
break;
|
||||
|
@ -466,12 +491,12 @@ function transport($_action, $_data = null) {
|
|||
}
|
||||
$transportdata = array();
|
||||
$stmt = $pdo->prepare("SELECT `id`,
|
||||
`is_mx_based`,
|
||||
`destination`,
|
||||
`nexthop`,
|
||||
`username`,
|
||||
`password`,
|
||||
`active`,
|
||||
`lookup_mx`,
|
||||
CONCAT(LEFT(`password`, 3), '...') AS `password_short`
|
||||
FROM `transports`
|
||||
WHERE `id` = :id");
|
||||
|
|
|
@ -3,7 +3,7 @@ function init_db_schema() {
|
|||
try {
|
||||
global $pdo;
|
||||
|
||||
$db_version = "25052021_0900";
|
||||
$db_version = "27052021_2000";
|
||||
|
||||
$stmt = $pdo->query("SHOW TABLES LIKE 'versions'");
|
||||
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
||||
|
@ -152,9 +152,9 @@ function init_db_schema() {
|
|||
"id" => "INT NOT NULL AUTO_INCREMENT",
|
||||
"destination" => "VARCHAR(255) NOT NULL",
|
||||
"nexthop" => "VARCHAR(255) NOT NULL",
|
||||
"username" => "VARCHAR(255) NOT NULL",
|
||||
"password" => "VARCHAR(255) NOT NULL",
|
||||
"lookup_mx" => "TINYINT(1) NOT NULL DEFAULT '1'",
|
||||
"username" => "VARCHAR(255) NOT NULL DEFAULT ''",
|
||||
"password" => "VARCHAR(255) NOT NULL DEFAULT ''",
|
||||
"is_mx_based" => "TINYINT(1) NOT NULL DEFAULT '0'",
|
||||
"active" => "TINYINT(1) NOT NULL DEFAULT '1'"
|
||||
),
|
||||
"keys" => array(
|
||||
|
|
|
@ -103,7 +103,7 @@ $(document).ready(function() {
|
|||
$(".hibp-out").after(res);
|
||||
}
|
||||
});
|
||||
$('[data-hibp]').after('<p class="small haveibeenpwned">↪ Check against haveibeenpwned.com</p><span class="hibp-out"></span>');
|
||||
$('[data-hibp]').after('<p class="small haveibeenpwned"><i class="bi bi-shield-fill-exclamation"></i> Check against haveibeenpwned.com</p><span class="hibp-out"></span>');
|
||||
$('[data-hibp]').on('input', function() {
|
||||
out_field = $(this).next('.haveibeenpwned').next('.hibp-out').text('').attr('class', 'hibp-out');
|
||||
});
|
||||
|
|
|
@ -187,9 +187,9 @@ jQuery(function($){
|
|||
{"name":"id","type":"text","title":"ID","style":{"width":"50px"}},
|
||||
{"name":"hostname","type":"text","title":lang.host,"style":{"width":"250px"}},
|
||||
{"name":"username","title":lang.username,"breakpoints":"xs sm"},
|
||||
{"name":"in_use_by","title":lang.in_use_by,"style":{"width":"110px"}, "type": "text","breakpoints":"xs sm"},
|
||||
{"name":"in_use_by","title":lang.in_use_by,"style":{"min-width":"200px","width":"200px"}, "type": "text","breakpoints":"xs sm"},
|
||||
{"name":"active","filterable": false,"style":{"maxWidth":"80px","width":"80px"},"title":lang.active,"formatter": function(value){return 1==value?'<i class="bi bi-check-lg"></i>':0==value&&'<i class="bi bi-x-lg"></i>';}},
|
||||
{"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right","maxWidth":"220px","width":"220px"},"type":"html","title":lang.action,"breakpoints":"xs sm md"}
|
||||
{"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right","min-width":"250px","width":"250px"},"type":"html","title":lang.action,"breakpoints":"xs sm md"}
|
||||
],
|
||||
"rows": $.ajax({
|
||||
dataType: 'json',
|
||||
|
@ -213,11 +213,11 @@ jQuery(function($){
|
|||
"columns": [
|
||||
{"name":"chkbox","title":"","style":{"maxWidth":"60px","width":"60px"},"filterable": false,"sortable": false,"type":"html"},
|
||||
{"name":"id","type":"text","title":"ID","style":{"width":"50px"}},
|
||||
{"name":"destination","type":"text","title":lang.destination,"style":{"width":"250px"}},
|
||||
{"name":"nexthop","type":"text","title":lang.nexthop,"style":{"width":"250px"}},
|
||||
{"name":"destination","type":"text","title":lang.destination,"style":{"min-width":"300px","width":"300px"}},
|
||||
{"name":"nexthop","type":"text","title":lang.nexthop,"style":{"min-width":"200px","width":"200px"}},
|
||||
{"name":"username","title":lang.username,"breakpoints":"xs sm"},
|
||||
{"name":"active","filterable": false,"style":{"maxWidth":"80px","width":"80px"},"title":lang.active,"formatter": function(value){return 1==value?'<i class="bi bi-check-lg"></i>':0==value&&'<i class="bi bi-x-lg"></i>';}},
|
||||
{"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right","maxWidth":"220px","width":"220px"},"type":"html","title":lang.action,"breakpoints":"xs sm md"}
|
||||
{"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right","min-width":"250px","width":"250px"},"type":"html","title":lang.action,"breakpoints":"xs sm md"}
|
||||
],
|
||||
"rows": $.ajax({
|
||||
dataType: 'json',
|
||||
|
@ -233,7 +233,12 @@ jQuery(function($){
|
|||
"empty": lang.empty,
|
||||
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
|
||||
"sorting": {"enabled": true},
|
||||
"toggleSelector": "table tbody span.footable-toggle"
|
||||
"toggleSelector": "table tbody span.footable-toggle",
|
||||
"on": {
|
||||
"ready.ft.table": function(e, ft){
|
||||
$('.mx-info').tooltip();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
function draw_queue() {
|
||||
|
@ -288,8 +293,11 @@ jQuery(function($){
|
|||
});
|
||||
} else if (table == 'transportstable') {
|
||||
$.each(data, function (i, item) {
|
||||
if (item.is_mx_based) {
|
||||
item.destination = '<i class="bi bi-info-circle-fill text-info mx-info" data-toggle="tooltip" title="' + lang.is_mx_based + '"></i> <code>' + item.destination + '</code>';
|
||||
}
|
||||
if (item.username) {
|
||||
item.username = '<span style="border-left:3px solid #' + intToRGB(hashCode(item.nexthop)) + ';padding-left:5px;">' + item.username + '</span>';
|
||||
item.username = '<i style="color:#' + intToRGB(hashCode(item.nexthop)) + ';" class="bi bi-square-fill"></i> ' + item.username;
|
||||
}
|
||||
item.action = '<div class="btn-group">' +
|
||||
'<a href="#" data-toggle="modal" data-target="#testTransportModal" data-transport-id="' + encodeURI(item.id) + '" data-transport-type="transport-map" class="btn btn-xs btn-default"><i class="bi bi-caret-right-fill"></i> Test</a>' +
|
||||
|
|
|
@ -156,7 +156,7 @@
|
|||
"change_logo": "Logo ändern",
|
||||
"configuration": "Konfiguration",
|
||||
"convert_html_to_text": "Konvertiere HTML zu reinem Text",
|
||||
"credentials_transport_warning": "<b>Warnung</b>: Das Hinzufügen einer neuen Regel bewirkt die Aktualisierung der Authentifizierungsdaten aller vorhandenen Einträge mit identischem Host.",
|
||||
"credentials_transport_warning": "<b>Warnung</b>: Das Hinzufügen einer neuen Regel bewirkt die Aktualisierung der Authentifizierungsdaten aller vorhandenen Einträge mit identischem Next Hop.",
|
||||
"customer_id": "Kunde",
|
||||
"customize": "UI-Anpassung",
|
||||
"delete_queue": "Alle löschen",
|
||||
|
@ -210,6 +210,7 @@
|
|||
"html": "HTML",
|
||||
"import": "Importieren",
|
||||
"import_private_key": "Private Key importieren",
|
||||
"is_mx_based": "MX-basiert",
|
||||
"in_use_by": "Verwendet von",
|
||||
"inactive": "Inaktiv",
|
||||
"include_exclude": "Ein- und Ausschlüsse",
|
||||
|
@ -220,7 +221,7 @@
|
|||
"link": "Link",
|
||||
"loading": "Bitte warten...",
|
||||
"logo_info": "Die hochgeladene Grafik wird für die Navigationsleiste auf eine Höhe von 40px skaliert. Für die Darstellung auf der Login-Maske beträgt die skalierte Breite maximal 250px. Eine frei skalierbare Grafik (etwa SVG) wird empfohlen.",
|
||||
"lookup_mx": "Ziel gegen MX prüfen (etwa .outlook.com, um alle Ziele mit MX *.outlook.com zu routen)",
|
||||
"lookup_mx": "Ziel mit MX vergleichen (Regex, etwa <code>.*google\\.com</code>, um alle Ziele mit MX *google.com zu routen)",
|
||||
"main_name": "\"mailcow UI\" Name",
|
||||
"merged_vars_hint": "Ausgegraute Reihen wurden aus der Datei <code>vars.(local.)inc.php</code> gelesen und können hier nicht verändert werden.",
|
||||
"message": "Nachricht",
|
||||
|
@ -232,7 +233,7 @@
|
|||
"no_record": "Kein Eintrag",
|
||||
"oauth2_client_id": "Client ID",
|
||||
"oauth2_client_secret": "Client Secret",
|
||||
"oauth2_info": "Die OAuth2 Implementierung unterstützt den Grant Type \"Authorization Code\" mit Refresh Tokens.<br>\r\nDer Server wird automatisch einen neuen Refresh Token ausstellen, sobald ein vorheriger Token gegen einen Access Token eingetauscht wurde.<br><br>\r\n→ Der Standard Scope lautet <i>profile</i>. Nur Mailbox-Benutzer können sich gegen OAuth2 authentifizieren. Wird kein Scope angegeben, verwendet das System per Standard <i>profile</i>.<br>\r\n→ Der <i>state</i> Parameter wird im Zuge des Autorisierungsprozesses benötigt.<br><br>\r\nDie Pfade für die OAuth2 API lauten wie folgt: <br>\r\n<ul>\r\n <li>Authorization Endpoint: <code>/oauth/authorize</code></li>\r\n <li>Token Endpoint: <code>/oauth/token</code></li>\r\n <li>Resource Page: <code>/oauth/profile</code></li>\r\n</ul>\r\nDie Regenerierung des Client Secrets wird vorhandene Authorization Codes nicht invalidieren, dennoch wird der Renew des Access Tokens durch einen Refresh Token nicht mehr gelingen.<br><br>\r\nDas Entfernen aller Client Tokens verursacht die umgehende Terminierung aller aktiven OAuth2 Sessions. Clients müssen sich erneut gegen die OAuth2 Anwendung authentifizieren.",
|
||||
"oauth2_info": "Die OAuth2 Implementierung unterstützt den Grant Type \"Authorization Code\" mit Refresh Tokens.<br>\r\nDer Server wird automatisch einen neuen Refresh Token ausstellen, sobald ein vorheriger Token gegen einen Access Token eingetauscht wurde.<br><br>\r\n• Der Standard Scope lautet <i>profile</i>. Nur Mailbox-Benutzer können sich gegen OAuth2 authentifizieren. Wird kein Scope angegeben, verwendet das System per Standard <i>profile</i>.<br>\r\n• Der <i>state</i> Parameter wird im Zuge des Autorisierungsprozesses benötigt.<br><br>\r\nDie Pfade für die OAuth2 API lauten wie folgt: <br>\r\n<ul>\r\n <li>Authorization Endpoint: <code>/oauth/authorize</code></li>\r\n <li>Token Endpoint: <code>/oauth/token</code></li>\r\n <li>Resource Page: <code>/oauth/profile</code></li>\r\n</ul>\r\nDie Regenerierung des Client Secrets wird vorhandene Authorization Codes nicht invalidieren, dennoch wird der Renew des Access Tokens durch einen Refresh Token nicht mehr gelingen.<br><br>\r\nDas Entfernen aller Client Tokens verursacht die umgehende Terminierung aller aktiven OAuth2 Sessions. Clients müssen sich erneut gegen die OAuth2 Anwendung authentifizieren.",
|
||||
"oauth2_redirect_uri": "Redirect-URI",
|
||||
"oauth2_renew_secret": "Neues Client Secret generieren",
|
||||
"oauth2_revoke_tokens": "Alle Client Tokens entfernen",
|
||||
|
@ -323,10 +324,10 @@
|
|||
"title": "Title",
|
||||
"title_name": "\"mailcow UI\" Webseiten Titel",
|
||||
"to_top": "Nach oben",
|
||||
"transport_dest_format": "Syntax: example.org, .example.org, *, box@example.org (mehrere Werte getrennt durch Komma einzugeben)",
|
||||
"transport_dest_format": "Regex oder Syntax: example.org, .example.org, *, box@example.org (getrennt durch Komma einzugeben)",
|
||||
"transport_maps": "Transport-Maps",
|
||||
"transports_hint": "→ Transport-Maps <b>überwiegen</b> senderabhängige Transport Maps.<br>\r\n→ Transport-Maps ignorieren Mailbox-Einstellungen für ausgehende Verschlüsselung. Eine serverweite TLS-Richtlinie wird jedoch angewendet.<br>\r\n→ Der Transport erfolgt immer via \"smtp:\", verwendet TLS wenn angeboten und unterstützt kein wrapped TLS (SMTPS).<br>\r\n→ Adressen, die mit \"/localhost$/\" übereinstimmen, werden immer via \"local:\" transportiert, daher sind sie von einer Zieldefinition \"*\" ausgeschlossen.<br>\r\n→ Die Authentifizierung wird anhand des \"Next hop\" Parameters ermittelt. Hierbei würde bei einem beispielhaften Wert \"[host]:25\" immer zuerst \"host\" abfragt und <b>erst im Anschluss</b> \"[host]:25\". Dieses Verhalten schließt die <b>gleichzeitige Verwendung</b> von Einträgen der Art \"host\" sowie \"[host]:25\" aus.",
|
||||
"transport_test_rcpt_info": "→ Die Verwendung von null@hosted.mailcow.de testet das Relay gegen ein fremdes Ziel.",
|
||||
"transports_hint": "• Transport-Maps <b>überwiegen</b> senderabhängige Transport Maps.<br>\r\n• MX-basierte Transporte werden bevorzugt.<br>\r\n• Transport-Maps ignorieren Mailbox-Einstellungen für ausgehende Verschlüsselung. Eine serverweite TLS-Richtlinie wird jedoch angewendet.<br>\r\n• Der Transport erfolgt immer via \"smtp:\", verwendet TLS wenn angeboten und unterstützt kein wrapped TLS (SMTPS).<br>\r\n• Adressen, die mit \"/localhost$/\" übereinstimmen, werden immer via \"local:\" transportiert, daher sind sie von einer Zieldefinition \"*\" ausgeschlossen.<br>\r\n• Die Authentifizierung wird anhand des \"Next hop\" Parameters ermittelt. Hierbei würde bei einem beispielhaften Wert \"[host]:25\" immer zuerst \"host\" abfragt und <b>erst im Anschluss</b> \"[host]:25\". Dieses Verhalten schließt die <b>gleichzeitige Verwendung</b> von Einträgen der Art \"host\" sowie \"[host]:25\" aus.",
|
||||
"transport_test_rcpt_info": "• Die Verwendung von null@hosted.mailcow.de testet das Relay gegen ein fremdes Ziel.",
|
||||
"ui_footer": "Footer (HTML zulässig)",
|
||||
"ui_header_announcement": "Ankündigungen",
|
||||
"ui_header_announcement_active": "Ankündigung aktivieren",
|
||||
|
@ -555,6 +556,7 @@
|
|||
"hostname": "Servername",
|
||||
"inactive": "Inaktiv",
|
||||
"kind": "Art",
|
||||
"lookup_mx": "Ziel mit MX vergleichen (Regex, etwa <code>.*google\\.com</code>, um alle Ziele mit MX *google.com zu routen)",
|
||||
"mailbox": "Mailbox bearbeiten",
|
||||
"mailbox_relayhost_info": "Wird auf eine Mailbox und direkte Alias-Adressen angewendet. Überschreibt die Einstellung einer Domain.",
|
||||
"mailbox_quota_def": "Standard-Quota einer Mailbox",
|
||||
|
@ -762,7 +764,7 @@
|
|||
"running": "In Ausführung",
|
||||
"set_postfilter": "Als Postfilter markieren",
|
||||
"set_prefilter": "Als Prefilter markieren",
|
||||
"sieve_info": "Es können mehrere Filter pro Benutzer existieren, aber nur ein Filter eines Typs (Pre-/Postfilter) kann gleichzeitig aktiv sein.<br>\r\nDie Ausführung erfolgt in nachstehender Reihenfolge. Ein fehlgeschlagenes Script sowie der Befehl \"keep;\" stoppen die weitere Verarbeitung <b>nicht</b>. Änderungen an globalen Sieve-Filtern bewirken einen Neustart von Dovecot.<br><br>Global sieve prefilter → Prefilter → User scripts → Postfilter → Global sieve postfilter",
|
||||
"sieve_info": "Es können mehrere Filter pro Benutzer existieren, aber nur ein Filter eines Typs (Pre-/Postfilter) kann gleichzeitig aktiv sein.<br>\r\nDie Ausführung erfolgt in nachstehender Reihenfolge. Ein fehlgeschlagenes Script sowie der Befehl \"keep;\" stoppen die weitere Verarbeitung <b>nicht</b>. Änderungen an globalen Sieve-Filtern bewirken einen Neustart von Dovecot.<br><br>Global sieve prefilter • Prefilter • User scripts • Postfilter • Global sieve postfilter",
|
||||
"sieve_preset_1": "E-Mails mit potenziell gefährlichen Dateitypen abweisen",
|
||||
"sieve_preset_2": "E-Mail eines bestimmten Absenders immer als gelesen markieren",
|
||||
"sieve_preset_3": "Lautlos löschen, weitere Ausführung von Filtern verhindern",
|
||||
|
|
|
@ -208,6 +208,7 @@
|
|||
"html": "HTML",
|
||||
"import": "Import",
|
||||
"import_private_key": "Import private key",
|
||||
"is_mx_based": "MX based",
|
||||
"in_use_by": "In use by",
|
||||
"inactive": "Inactive",
|
||||
"include_exclude": "Include/Exclude",
|
||||
|
@ -218,7 +219,7 @@
|
|||
"link": "Link",
|
||||
"loading": "Please wait...",
|
||||
"logo_info": "Your image will be scaled to a height of 40px for the top navigation bar and a max. width of 250px for the start page. A scalable graphic is highly recommended.",
|
||||
"lookup_mx": "Match destination against MX (.outlook.com to route all mail targeted to a MX *.outlook.com over this hop)",
|
||||
"lookup_mx": "Destination is a regular expression to match against MX name (<code>.*google\\.com</code> to route all mail targeted to a MX ending in google.com over this hop)",
|
||||
"main_name": "\"mailcow UI\" name",
|
||||
"merged_vars_hint": "Greyed out rows were merged from <code>vars.(local.)inc.php</code> and cannot be modified.",
|
||||
"message": "Message",
|
||||
|
@ -230,7 +231,7 @@
|
|||
"no_record": "No record",
|
||||
"oauth2_client_id": "Client ID",
|
||||
"oauth2_client_secret": "Client secret",
|
||||
"oauth2_info": "The OAuth2 implementation supports the grant type \"Authorization Code\" and issues refresh tokens.<br>\r\nThe server also automatically issues new refresh tokens, after a refresh token has been used.<br><br>\r\n→ The default scope is <i>profile</i>. Only mailbox users can be authenticated against OAuth2. If the scope parameter is omitted, it falls back to <i>profile</i>.<br>\r\n→ The <i>state</i> parameter is required to be sent by the client as part of the authorize request.<br><br>\r\nPaths for requests to the OAuth2 API: <br>\r\n<ul>\r\n <li>Authorization endpoint: <code>/oauth/authorize</code></li>\r\n <li>Token endpoint: <code>/oauth/token</code></li>\r\n <li>Resource page: <code>/oauth/profile</code></li>\r\n</ul>\r\nRegenerating the client secret will not expire existing authorization codes, but they will fail to renew their token.<br><br>\r\nRevoking client tokens will cause immediate termination of all active sessions. All clients need to re-authenticate.",
|
||||
"oauth2_info": "The OAuth2 implementation supports the grant type \"Authorization Code\" and issues refresh tokens.<br>\r\nThe server also automatically issues new refresh tokens, after a refresh token has been used.<br><br>\r\n• The default scope is <i>profile</i>. Only mailbox users can be authenticated against OAuth2. If the scope parameter is omitted, it falls back to <i>profile</i>.<br>\r\n• The <i>state</i> parameter is required to be sent by the client as part of the authorize request.<br><br>\r\nPaths for requests to the OAuth2 API: <br>\r\n<ul>\r\n <li>Authorization endpoint: <code>/oauth/authorize</code></li>\r\n <li>Token endpoint: <code>/oauth/token</code></li>\r\n <li>Resource page: <code>/oauth/profile</code></li>\r\n</ul>\r\nRegenerating the client secret will not expire existing authorization codes, but they will fail to renew their token.<br><br>\r\nRevoking client tokens will cause immediate termination of all active sessions. All clients need to re-authenticate.",
|
||||
"oauth2_redirect_uri": "Redirect URI",
|
||||
"oauth2_renew_secret": "Generate new client secret",
|
||||
"oauth2_revoke_tokens": "Revoke all client tokens",
|
||||
|
@ -321,10 +322,10 @@
|
|||
"title": "Title",
|
||||
"title_name": "\"mailcow UI\" website title",
|
||||
"to_top": "Back to top",
|
||||
"transport_dest_format": "Syntax: example.org, .example.org, *, box@example.org (multiple values can be comma-separated)",
|
||||
"transport_dest_format": "Regex or syntax: example.org, .example.org, *, box@example.org (multiple values can be comma-separated)",
|
||||
"transport_maps": "Transport Maps",
|
||||
"transports_hint": "→ A transport map entry <b>overrules</b> a sender-dependent transport map</b>.<br>\r\n→ Outbound TLS policy settings per-user are ignored and can only be enforced by TLS policy map entries.<br>\r\n→ The transport service for defined transports is always \"smtp:\" and will therefore try TLS when offered. Wrapped TLS (SMTPS) is not supported.<br>\r\n→ Addresses matching \"/localhost$/\" will always be transported via \"local:\", therefore a \"*\" destination will not apply to those addresses.<br>\r\n→ To determine credentials for an exemplary next hop \"[host]:25\", Postfix <b>always</b> queries for \"host\" before searching for \"[host]:25\". This behavior makes it impossible to use \"host\" and \"[host]:25\" at the same time.",
|
||||
"transport_test_rcpt_info": "→ Use null@hosted.mailcow.de to test relaying to a foreign destination.",
|
||||
"transports_hint": "• A transport map entry <b>overrules</b> a sender-dependent transport map</b>.<br>\r\n• MX-based transports are preferably used.<br>\r\n• Outbound TLS policy settings per-user are ignored and can only be enforced by TLS policy map entries.<br>\r\n• The transport service for defined transports is always \"smtp:\" and will therefore try TLS when offered. Wrapped TLS (SMTPS) is not supported.<br>\r\n• Addresses matching \"/localhost$/\" will always be transported via \"local:\", therefore a \"*\" destination will not apply to those addresses.<br>\r\n• To determine credentials for an exemplary next hop \"[host]:25\", Postfix <b>always</b> queries for \"host\" before searching for \"[host]:25\". This behavior makes it impossible to use \"host\" and \"[host]:25\" at the same time.",
|
||||
"transport_test_rcpt_info": "• Use null@hosted.mailcow.de to test relaying to a foreign destination.",
|
||||
"ui_footer": "Footer (HTML allowed)",
|
||||
"ui_header_announcement": "Announcements",
|
||||
"ui_header_announcement_active": "Set announcement active",
|
||||
|
@ -553,6 +554,7 @@
|
|||
"hostname": "Hostname",
|
||||
"inactive": "Inactive",
|
||||
"kind": "Kind",
|
||||
"lookup_mx": "Destination is a regular expression to match against MX name (<code>.*google\\.com</code> to route all mail targeted to a MX ending in google.com over this hop)",
|
||||
"mailbox": "Edit mailbox",
|
||||
"mailbox_quota_def": "Default mailbox quota",
|
||||
"mailbox_relayhost_info": "Applied to the mailbox and direct aliases only, does override a domain relayhost.",
|
||||
|
@ -760,7 +762,7 @@
|
|||
"running": "Running",
|
||||
"set_postfilter": "Mark as postfilter",
|
||||
"set_prefilter": "Mark as prefilter",
|
||||
"sieve_info": "You can store multiple filters per user, but only one prefilter and one postfilter can be active at the same time.<br>\r\nEach filter will be processed in the described order. Neither a failed script nor an issued \"keep;\" will stop processing of further scripts. Changes to global sieve scripts will trigger a restart of Dovecot.<br><br>Global sieve prefilter → Prefilter → User scripts → Postfilter → Global sieve postfilter",
|
||||
"sieve_info": "You can store multiple filters per user, but only one prefilter and one postfilter can be active at the same time.<br>\r\nEach filter will be processed in the described order. Neither a failed script nor an issued \"keep;\" will stop processing of further scripts. Changes to global sieve scripts will trigger a restart of Dovecot.<br><br>Global sieve prefilter • Prefilter • User scripts • Postfilter • Global sieve postfilter",
|
||||
"sieve_preset_1": "Discard mail with probable dangerous file types",
|
||||
"sieve_preset_2": "Always mark the e-mail of a specific sender as seen",
|
||||
"sieve_preset_3": "Discard silently, stop all further sieve processing",
|
||||
|
|
|
@ -290,7 +290,7 @@ services:
|
|||
- dovecot
|
||||
|
||||
postfix-mailcow:
|
||||
image: mailcow/postfix:1.62
|
||||
image: mailcow/postfix:1.63
|
||||
depends_on:
|
||||
- mysql-mailcow
|
||||
volumes:
|
||||
|
|
Loading…
Reference in New Issue