[Web, Postfix, Compose] Allow to add relayhosts per domain (+ plain and login authentication)

master
andryyy 2017-07-22 20:39:54 +02:00
parent ed33cb5f57
commit 83d485dd94
15 changed files with 538 additions and 13 deletions

View File

@ -20,12 +20,26 @@ dbname = ${DBNAME}
query = SELECT IF( EXISTS( SELECT 'TLS_ACTIVE' FROM alias LEFT OUTER JOIN mailbox ON mailbox.username = alias.goto WHERE (address='%s' OR address IN (SELECT CONCAT('%u', '@', target_domain) FROM alias_domain WHERE alias_domain='%d')) AND mailbox.tls_enforce_in = '1' AND mailbox.active = '1'), 'reject_plaintext_session', NULL) AS 'tls_enforce_in';
EOF
cat <<EOF > /opt/postfix/conf/sql/mysql_tls_enforce_out_policy.cf
cat <<EOF > /opt/postfix/conf/sql/mysql_sender_dependent_default_transport_maps.cf
user = ${DBUSER}
password = ${DBPASS}
hosts = mysql
dbname = ${DBNAME}
query = SELECT IF( EXISTS( SELECT 'TLS_ACTIVE' FROM alias LEFT OUTER JOIN mailbox ON mailbox.username = alias.goto WHERE (address='%s' OR address IN (SELECT CONCAT('%u', '@', target_domain) FROM alias_domain WHERE alias_domain='%d')) AND mailbox.tls_enforce_out = '1' AND mailbox.active = '1'), 'smtp_enforced_tls:', NULL) AS 'tls_enforce_out';
query = SELECT GROUP_CONCAT(transport SEPARATOR '') AS transport_maps
FROM (
SELECT IF(EXISTS(SELECT 'smtp_type' FROM alias LEFT OUTER JOIN mailbox ON mailbox.username = alias.goto WHERE (address = '%s' OR address IN (SELECT CONCAT('%u', '@', target_domain) FROM alias_domain WHERE alias_domain = '%d')) AND mailbox.tls_enforce_out = '1' AND mailbox.active = '1'), 'smtp_enforced_tls:', 'smtp:') AS 'transport'
UNION ALL
SELECT hostname AS transport FROM relayhosts LEFT OUTER JOIN domain ON domain.relayhost = relayhosts.id WHERE relayhosts.active = '1' AND domain = '%d' OR domain IN (SELECT target_domain FROM alias_domain WHERE alias_domain = '%d')
)
AS transport_view;
EOF
cat <<EOF > /opt/postfix/conf/sql/mysql_sasl_passwd_maps.cf
user = ${DBUSER}
password = ${DBPASS}
hosts = mysql
dbname = ${DBNAME}
query = SELECT CONCAT_WS(':', username, password) AS auth_data FROM relayhosts WHERE id IN (SELECT relayhost FROM domain WHERE CONCAT('@', domain) = '%s');
EOF
cat <<EOF > /opt/postfix/conf/sql/mysql_virtual_alias_domain_catchall_maps.cf
@ -110,6 +124,5 @@ if [[ $? != 0 ]]; then
exit 1
else
postfix -c /opt/postfix/conf start
supervisorctl restart postfix-maillog
sleep 126144000
fi

View File

@ -39,11 +39,11 @@ postscreen_greet_ttl = 2d
postscreen_greet_wait = 3s
postscreen_non_smtp_command_enable = no
postscreen_pipelining_enable = no
proxy_read_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_sender_acl.cf, proxy:mysql:/opt/postfix/conf/sql/mysql_tls_enforce_out_policy.cf, proxy:mysql:/opt/postfix/conf/sql/mysql_tls_enforce_in_policy.cf, $local_recipient_maps $mydestination $virtual_alias_maps $virtual_alias_domains $virtual_mailbox_maps $virtual_mailbox_domains $relay_recipient_maps $relay_domains $canonical_maps $sender_canonical_maps $recipient_canonical_maps $relocated_maps $transport_maps $mynetworks $smtpd_sender_login_maps
proxy_read_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_sender_acl.cf, proxy:mysql:/opt/postfix/conf/sql/mysql_sender_dependent_default_transport_maps.cf, proxy:mysql:/opt/postfix/conf/sql/mysql_sasl_passwd_maps.cf, proxy:mysql:/opt/postfix/conf/sql/mysql_tls_enforce_in_policy.cf, $local_recipient_maps $mydestination $virtual_alias_maps $virtual_alias_domains $virtual_mailbox_maps $virtual_mailbox_domains $relay_recipient_maps $relay_domains $canonical_maps $sender_canonical_maps $recipient_canonical_maps $relocated_maps $transport_maps $mynetworks $smtpd_sender_login_maps
queue_run_delay = 300s
relay_domains = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_relay_domain_maps.cf
relay_recipient_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_relay_recipient_maps.cf
sender_dependent_default_transport_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_tls_enforce_out_policy.cf
sender_dependent_default_transport_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_sender_dependent_default_transport_maps.cf
smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt
smtp_tls_cert_file = /etc/ssl/mail/cert.pem
smtp_tls_key_file = /etc/ssl/mail/key.pem
@ -94,3 +94,8 @@ mydestination = localhost.localdomain, localhost
#content_filter=zeyple
# Prefere IPv4, useful for v4-only envs
smtp_address_preference = ipv4
smtp_sender_dependent_authentication = yes
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_sasl_passwd_maps.cf
smtp_sasl_security_options =
smtp_sasl_mechanism_filter = plain, login

View File

@ -121,6 +121,18 @@ $tfa_data = get_tfa();
<div role="tabpanel" class="tab-pane" id="tab-config">
<div class="row">
<div class="col-sm-2 hidden-xs">
<div id="scrollbox" class="list-group">
<a href="#dkim" class="list-group-item"><?=$lang['admin']['dkim_keys'];?></a>
<a href="#fwdhosts" class="list-group-item"><?=$lang['admin']['forwarding_hosts'];?></a>
<a href="#f2bparams" class="list-group-item"><?=$lang['admin']['f2b_parameters'];?></a>
<a href="#relayhosts" class="list-group-item">Relayhosts</a>
<a href="#top" class="list-group-item" style="border-top:1px dashed #dadada"> <?=$lang['admin']['to_top'];?></a>
</div>
</div>
<div class="col-sm-10">
<span class="anchor" id="dkim"></span>
<div class="panel panel-default">
<div class="panel-heading"><?=$lang['admin']['dkim_keys'];?></div>
<div class="panel-body">
@ -254,6 +266,7 @@ XYZ
</div>
</div>
<span class="anchor" id="fwdhosts"></span>
<div class="panel panel-default">
<div class="panel-heading"><?=$lang['admin']['forwarding_hosts'];?></div>
<div class="panel-body">
@ -291,6 +304,7 @@ XYZ
</div>
</div>
<span class="anchor" id="f2bparams"></span>
<div class="panel panel-default">
<div class="panel-heading"><?=$lang['admin']['f2b_parameters'];?></div>
<div class="panel-body">
@ -318,6 +332,48 @@ XYZ
</form>
</div>
</div>
<span class="anchor" id="relayhosts"></span>
<div class="panel panel-default">
<div class="panel-heading">Relayhosts</div>
<div class="panel-body">
<p style="margin-bottom:40px"><?=$lang['admin']['relayhosts_hint'];?></p>
<div class="table-responsive">
<table class="table table-striped table-condensed" id="relayhoststable"></table>
</div>
<div class="mass-actions-admin">
<div class="btn-group btn-group-sm">
<button type="button" id="toggle_multi_select_all" data-id="rlyhosts" class="btn btn-default"><?=$lang['mailbox']['toggle_all'];?></button>
<a class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" href="#"><?=$lang['mailbox']['quick_actions'];?> <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a id="edit_selected" data-id="rlyhosts" data-api-url='edit/relayhost' data-api-attr='{"active":"1"}' href="#"><?=$lang['mailbox']['activate'];?></a></li>
<li><a id="edit_selected" data-id="rlyhosts" data-api-url='edit/relayhost' data-api-attr='{"active":"0"}' href="#"><?=$lang['mailbox']['deactivate'];?></a></li>
<li role="separator" class="divider"></li>
<li><a id="delete_selected" data-id="rlyhosts" data-api-url='delete/relayhost' href="#"><?=$lang['admin']['remove'];?></a></li>
</ul>
</div>
</div>
<legend><?=$lang['admin']['add_relayhost'];?></legend>
<p class="help-block"><?=$lang['admin']['add_relayhost_add_hint'];?></p>
<form class="form-inline" data-id="rlyhost" role="form" method="post">
<div class="form-group">
<label for="hostname"><?=$lang['admin']['host'];?></label>
<input class="form-control" id="hostname" name="hostname" required>
</div>
<div class="form-group">
<label for="hostname"><?=$lang['admin']['username'];?></label>
<input class="form-control" id="username" name="username">
</div>
<div class="form-group">
<label for="hostname"><?=$lang['admin']['password'];?></label>
<input class="form-control" id="password" name="password">
</div>
<button class="btn btn-default" id="add_item" data-id="rlyhost" data-api-url='add/relayhost' data-api-attr='{}' href="#"><span class="glyphicon glyphicon-plus"></span> <?=$lang['admin']['add'];?></button>
</form>
</div>
</div>
</div>
</div>
</div>
<div role="tabpanel" class="tab-pane" id="tab-postfix-logs">

View File

@ -41,4 +41,15 @@ body.modal-open {
-moz-transform:rotateX(180deg);
-webkit-transform:rotateX(180deg);
transform:rotateX(180deg);
}
}
.anchor {
display: block;
height: 65px;
margin-top: -65px;
visibility: hidden;
}
.scrollboxFixed {
position: fixed;
top: 65px;
z-index: 1;
}

View File

@ -138,6 +138,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
$domain = $_GET["domain"];
$result = mailbox('get', 'domain_details', $domain);
$rl = mailbox('get', 'domain_ratelimit', $domain);
$rlyhosts = relayhost('get');
if (!empty($result)) {
?>
<h4><?=$lang['edit']['domain'];?></h4>
@ -178,6 +179,21 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
<input type="number" class="form-control" name="quota" id="quota" value="<?=intval($result['max_quota_for_domain'] / 1048576);?>">
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-2" for="quota">Relayhost</label>
<div class="col-sm-10">
<select name="relayhost" id="relayhost" class="form-control">
<?php
foreach ($rlyhosts as $rlyhost) {
?>
<option value="<?=$rlyhost['id'];?>" <?=($result['relayhost'] == $rlyhost['id']) ? 'selected' : null;?>>ID <?=$rlyhost['id'];?>: <?=$rlyhost['hostname'];?> (<?=$rlyhost['username'];?>)</option>
<?php
}
?>
<option value="" <?=($result['relayhost'] == "0") ? 'selected' : null;?>>None</option>
</select>
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-2"><?=$lang['edit']['backup_mx_options'];?></label>
<div class="col-sm-10">

View File

@ -260,8 +260,8 @@ function mailbox($_action, $_type, $_data = null) {
return false;
}
try {
$stmt = $pdo->prepare("INSERT INTO `domain` (`domain`, `description`, `aliases`, `mailboxes`, `maxquota`, `quota`, `transport`, `backupmx`, `active`, `relay_all_recipients`)
VALUES (:domain, :description, :aliases, :mailboxes, :maxquota, :quota, 'virtual', :backupmx, :active, :relay_all_recipients)");
$stmt = $pdo->prepare("INSERT INTO `domain` (`domain`, `description`, `aliases`, `mailboxes`, `maxquota`, `quota`, `backupmx`, `active`, `relay_all_recipients`)
VALUES (:domain, :description, :aliases, :mailboxes, :maxquota, :quota, :backupmx, :active, :relay_all_recipients)");
$stmt->execute(array(
':domain' => $domain,
':description' => $description,
@ -1441,6 +1441,7 @@ function mailbox($_action, $_type, $_data = null) {
$active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active_int'];
$backupmx = (isset($_data['backupmx'])) ? intval($_data['backupmx']) : $is_now['backupmx_int'];
$relay_all_recipients = (isset($_data['relay_all_recipients'])) ? intval($_data['relay_all_recipients']) : $is_now['relay_all_recipients_int'];
$relayhost = (isset($_data['relayhost'])) ? intval($_data['relayhost']) : $is_now['relayhost'];
$aliases = (!empty($_data['aliases'])) ? $_data['aliases'] : $is_now['max_num_aliases_for_domain'];
$mailboxes = (!empty($_data['mailboxes'])) ? $_data['mailboxes'] : $is_now['max_num_mboxes_for_domain'];
$maxquota = (!empty($_data['maxquota'])) ? $_data['maxquota'] : ($is_now['max_quota_for_mbox'] / 1048576);
@ -1531,6 +1532,7 @@ function mailbox($_action, $_type, $_data = null) {
`active` = :active,
`quota` = :quota,
`maxquota` = :maxquota,
`relayhost` = :relayhost,
`mailboxes` = :mailboxes,
`aliases` = :aliases,
`description` = :description
@ -1541,6 +1543,7 @@ function mailbox($_action, $_type, $_data = null) {
':active' => $active,
':quota' => $quota,
':maxquota' => $maxquota,
':relayhost' => $relayhost,
':mailboxes' => $mailboxes,
':aliases' => $aliases,
':description' => $description,
@ -2470,7 +2473,7 @@ function mailbox($_action, $_type, $_data = null) {
':domain' => $_data
));
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (!empty($row)) {
if (!empty($row)) {
$_data = $row['target_domain'];
}
$stmt = $pdo->prepare("SELECT
@ -2480,6 +2483,7 @@ function mailbox($_action, $_type, $_data = null) {
`mailboxes`,
`maxquota`,
`quota`,
`relayhost`,
`relay_all_recipients` as `relay_all_recipients_int`,
`backupmx` as `backupmx_int`,
`active` as `active_int`,
@ -2514,6 +2518,7 @@ function mailbox($_action, $_type, $_data = null) {
$domaindata['max_num_mboxes_for_domain'] = $row['mailboxes'];
$domaindata['max_quota_for_mbox'] = $row['maxquota'] * 1048576;
$domaindata['max_quota_for_domain'] = $row['quota'] * 1048576;
$domaindata['relayhost'] = $row['relayhost'];
$domaindata['backupmx'] = $row['backupmx'];
$domaindata['backupmx_int'] = $row['backupmx_int'];
$domaindata['active'] = $row['active'];

View File

@ -0,0 +1,179 @@
<?php
function relayhost($_action, $_data = null) {
global $pdo;
global $lang;
switch ($_action) {
case 'add':
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => sprintf($lang['danger']['access_denied'])
);
return false;
}
$hostname = trim($_data['hostname']);
$username = str_replace(':', '\:', trim($_data['username']));
$password = str_replace(':', '\:', trim($_data['password']));
if (empty($hostname)) {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => 'Invalid host specified: '. htmlspecialchars($host)
);
return false;
}
try {
$stmt = $pdo->prepare("INSERT INTO `relayhosts` (`hostname`, `username` ,`password`, `active`)
VALUES (:hostname, :username, :password, :active)");
$stmt->execute(array(
':hostname' => $hostname,
':username' => $username,
':password' => $password,
':active' => '1'
));
}
catch (PDOException $e) {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => 'MySQL: '.$e
);
return false;
}
$_SESSION['return'] = array(
'type' => 'success',
'msg' => sprintf($lang['success']['relayhost_added'], htmlspecialchars(implode(', ', $hosts)))
);
break;
case 'edit':
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => sprintf($lang['danger']['access_denied'])
);
return false;
}
$ids = (array)$_data['id'];
foreach ($ids as $id) {
$is_now = relayhost('details', $id);
if (!empty($is_now)) {
$hostname = (!empty($_data['hostname'])) ? trim($_data['hostname']) : $is_now['hostname'];
$username = (!empty($_data['username'])) ? trim($_data['username']) : $is_now['username'];
$password = (!empty($_data['password'])) ? trim($_data['password']) : $is_now['password'];
$active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active_int'];
}
else {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => 'Relayhost invalid'
);
return false;
}
try {
$stmt = $pdo->prepare("UPDATE `relayhosts` SET
`hostname` = :hostname,
`username` = :username,
`password` = :password,
`active` = :active
WHERE `id` = :id");
$stmt->execute(array(
':id' => $id,
':hostname' => $hostname,
':username' => $username,
':password' => $password,
':active' => $active
));
}
catch (PDOException $e) {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => 'MySQL: '.$e
);
return false;
}
}
$_SESSION['return'] = array(
'type' => 'success',
'msg' => sprintf($lang['success']['object_modified'], htmlspecialchars(implode(', ', $hostnames)))
);
break;
case 'delete':
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => sprintf($lang['danger']['access_denied'])
);
return false;
}
$ids = (array)$_data['id'];
foreach ($ids as $id) {
try {
$stmt = $pdo->prepare("DELETE FROM `relayhosts` WHERE `id`= :id");
$stmt->execute(array(':id' => $id));
$stmt = $pdo->prepare("UPDATE `domain` SET `relayhost` = '0' WHERE `relayhost`= :id");
$stmt->execute(array(':id' => $id));
}
catch (PDOException $e) {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => 'MySQL: '.$e
);
return false;
}
}
$_SESSION['return'] = array(
'type' => 'success',
'msg' => sprintf($lang['success']['relayhost_removed'], htmlspecialchars(implode(', ', $hostnames)))
);
break;
case 'get':
if ($_SESSION['mailcow_cc_role'] != "admin") {
return false;
}
$relayhosts = array();
try {
$stmt = $pdo->query("SELECT `id`, `hostname`, `username` FROM `relayhosts`");
$relayhosts = $stmt->fetchAll(PDO::FETCH_ASSOC);
}
catch(PDOException $e) {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => 'MySQL: '.$e
);
}
return $relayhosts;
break;
case 'details':
if ($_SESSION['mailcow_cc_role'] != "admin" || !isset($_data)) {
return false;
}
$relayhostdata = array();
try {
$stmt = $pdo->prepare("SELECT `id`,
`hostname`,
`username`,
`password`,
`active` AS `active_int`,
CONCAT(LEFT(`password`, 3), '...') AS `password_short`,
CASE `active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `active`
FROM `relayhosts`
WHERE `id` = :id");
$stmt->execute(array(':id' => $_data));
$relayhostdata = $stmt->fetch(PDO::FETCH_ASSOC);
if (!empty($relayhostdata)) {
$stmt = $pdo->prepare("SELECT GROUP_CONCAT(`domain` SEPARATOR ', ') AS `used_by_domains` FROM `domain` WHERE `relayhost` = :id");
$stmt->execute(array(':id' => $_data));
$used_by_domains = $stmt->fetch(PDO::FETCH_ASSOC)['used_by_domains'];
$used_by_domains = (empty($used_by_domains)) ? '' : $used_by_domains;
$relayhostdata['used_by_domains'] = $used_by_domains;
}
}
catch(PDOException $e) {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => 'MySQL: '.$e
);
}
return $relayhostdata;
break;
}
}

View File

@ -29,7 +29,7 @@
<link rel="shortcut icon" href="/favicon.png" type="image/png">
<link rel="icon" href="/favicon.png" type="image/png">
</head>
<body style="padding-top: 70px;">
<body style="padding-top: 70px;" id="top">
<nav class="navbar navbar-default navbar-fixed-top" role="navigation">
<div class="container-fluid">
<div class="navbar-header">

View File

@ -3,7 +3,7 @@ function init_db_schema() {
try {
global $pdo;
$db_version = "18052017_1017";
$db_version = "20072107_1029";
$stmt = $pdo->query("SHOW TABLES LIKE 'versions'");
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
@ -60,6 +60,24 @@ function init_db_schema() {
),
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
),
"relayhosts" => array(
"cols" => array(
"id" => "INT NOT NULL AUTO_INCREMENT",
"hostname" => "VARCHAR(255) NOT NULL",
"username" => "VARCHAR(255) NOT NULL",
"password" => "VARCHAR(255) NOT NULL",
"active" => "TINYINT(1) NOT NULL DEFAULT '1'"
),
"keys" => array(
"primary" => array(
"" => array("id")
),
"key" => array(
"hostname" => array("hostname")
)
),
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
),
"alias" => array(
"cols" => array(
"address" => "VARCHAR(255) NOT NULL",
@ -95,7 +113,7 @@ function init_db_schema() {
"mailboxes" => "INT(10) NOT NULL DEFAULT '0'",
"maxquota" => "BIGINT(20) NOT NULL DEFAULT '0'",
"quota" => "BIGINT(20) NOT NULL DEFAULT '102400'",
"transport" => "VARCHAR(255) NOT NULL",
"relayhost" => "VARCHAR(255) NOT NULL DEFAULT '0'",
"backupmx" => "TINYINT(1) NOT NULL DEFAULT '0'",
"relay_all_recipients" => "TINYINT(1) NOT NULL DEFAULT '0'",
"created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)",

View File

@ -65,6 +65,7 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.domain_admin.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.policy.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.dkim.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.fwdhost.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.relayhost.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.fail2ban.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/init_db.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/triggers.inc.php';

View File

@ -328,6 +328,44 @@ jQuery(function($){
}
});
}
function draw_relayhosts() {
ft_forwardinghoststable = FooTable.init('#relayhoststable', {
"columns": [
{"name":"chkbox","title":"","style":{"maxWidth":"40px","width":"40px"},"filterable": false,"sortable": false,"type":"html"},
{"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":"used_by_domains","title":lang.in_use_by, "type": "text","breakpoints":"xs sm"},
{"name":"active","filterable": false,"style":{"maxWidth":"80px","width":"80px"},"title":lang.active},
{"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right","maxWidth":"180px","width":"180px"},"type":"html","title":lang.action,"breakpoints":"xs sm"}
],
"rows": $.ajax({
dataType: 'json',
url: '/api/v1/get/relayhost/all',
jsonp: false,
error: function () {
console.log('Cannot draw forwarding hosts table');
},
success: function (data) {
$.each(data, function (i, item) {
item.action = '<div class="btn-group">' +
'<a href="#" id="delete_selected" data-id="single-rlshost" data-api-url="delete/relayhost" data-item="' + encodeURI(item.id) + '" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> ' + lang.remove + '</a>' +
'</div>';
item.chkbox = '<input type="checkbox" data-id="rlyhosts" name="multi_select" value="' + item.id + '" />';
});
}
}),
"empty": lang.empty,
"paging": {
"enabled": true,
"limit": 5,
"size": log_pagination_size
},
"sorting": {
"enabled": true
}
});
}
function draw_rspamd_history() {
ft_postfix_logs = FooTable.init('#rspamd_history', {
"columns": [{
@ -504,5 +542,20 @@ jQuery(function($){
draw_fail2ban_logs();
draw_domain_admins();
draw_fwd_hosts();
draw_relayhosts();
draw_rspamd_history();
});
$(window).load(function(){
width = $("#scrollbox").width();
$(window).bind('scroll', function() {
if ($(window).scrollTop() > 70) {
$('#scrollbox').addClass('scrollboxFixed');
$("#scrollbox").css("width", width);
} else {
width = $("#scrollbox").width();
$('#scrollbox').removeClass('scrollboxFixed');
$("#scrollbox").removeAttr("style");
}
});
});

View File

@ -60,6 +60,39 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
));
}
break;
case "relayhost":
if (isset($_POST['attr'])) {
$attr = (array)json_decode($_POST['attr'], true);
if (relayhost('add', $attr) === false) {
if (isset($_SESSION['return'])) {
echo json_encode($_SESSION['return']);
}
else {
echo json_encode(array(
'type' => 'error',
'msg' => 'Cannot add item'
));
}
}
else {
if (isset($_SESSION['return'])) {
echo json_encode($_SESSION['return']);
}
else {
echo json_encode(array(
'type' => 'success',
'msg' => 'Task completed'
));
}
}
}
else {
echo json_encode(array(
'type' => 'error',
'msg' => 'Cannot find attributes in post data'
));
}
break;
case "mailbox":
if (isset($_POST['attr'])) {
$attr = (array)json_decode($_POST['attr'], true);
@ -496,6 +529,42 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
break;
}
break;
case "relayhost":
switch ($object) {
case "all":
$relayhosts = relayhost('get');
if (!empty($relayhosts)) {
foreach ($relayhosts as $relayhost) {
if ($details = relayhost('details', $relayhost['id'])) {
$data[] = $details;
}
else {
continue;
}
}
if (!isset($data) || empty($data)) {
echo '{}';
}
else {
echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
}
}
else {
echo '{}';
}
break;
default:
$data = relayhost('details', $object);
if (!isset($data) || empty($data)) {
echo '{}';
}
else {
echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
}
break;
}
break;
case "logs":
switch ($object) {
case "dovecot":
@ -930,6 +999,47 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
));
}
break;
case "relayhost":
if (isset($_POST['items'])) {
$items = (array)json_decode($_POST['items'], true);
if (is_array($items)) {
if (relayhost('delete', array('id' => $items)) === false) {
if (isset($_SESSION['return'])) {
echo json_encode($_SESSION['return']);
}
else {
echo json_encode(array(
'type' => 'error',
'msg' => 'Deletion of items/s failed'
));
}
}
else {
if (isset($_SESSION['return'])) {
echo json_encode($_SESSION['return']);
}
else {
echo json_encode(array(
'type' => 'success',
'msg' => 'Task completed'
));
}
}
}
else {
echo json_encode(array(
'type' => 'error',
'msg' => 'Cannot find id array in post data'
));
}
}
else {
echo json_encode(array(
'type' => 'error',
'msg' => 'Cannot find items in post data'
));
}
break;
case "syncjob":
if (isset($_POST['items'])) {
$items = (array)json_decode($_POST['items'], true);
@ -1470,6 +1580,50 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
));
}
break;
case "relayhost":
if (isset($_POST['items']) && isset($_POST['attr'])) {
$items = (array)json_decode($_POST['items'], true);
$attr = (array)json_decode($_POST['attr'], true);
$postarray = array_merge(array('id' => $items), $attr);
if (is_array($postarray['id'])) {
if (relayhost('edit', $postarray) === false) {
if (isset($_SESSION['return'])) {
echo json_encode($_SESSION['return']);
}
else {
echo json_encode(array(
'type' => 'error',
'msg' => 'Edit failed'
));
}
exit();
}
else {
if (isset($_SESSION['return'])) {
echo json_encode($_SESSION['return']);
}
else {
echo json_encode(array(
'type' => 'success',
'msg' => 'Task completed'
));
}
}
}
else {
echo json_encode(array(
'type' => 'error',
'msg' => 'Incomplete post data'
));
}
}
else {
echo json_encode(array(
'type' => 'error',
'msg' => 'Incomplete post data'
));
}
break;
case "delimiter_action":
if (isset($_POST['items']) && isset($_POST['attr'])) {
$items = (array)json_decode($_POST['items'], true);

View File

@ -485,13 +485,20 @@ $lang['admin']['time'] = 'Zeit';
$lang['admin']['priority'] = 'Gewichtung';
$lang['admin']['refresh'] = 'Neu laden';
$lang['admin']['logs'] = 'Logs';
$lang['admin']['to_top'] = 'Nach oben';
$lang['admin']['in_use_by'] = 'Verwendet von';
$lang['admin']['message'] = 'Nachricht';
$lang['admin']['forwarding_hosts'] = 'Weiterleitungs-Hosts';
$lang['admin']['forwarding_hosts_hint'] = 'Eingehende Nachrichten werden von den hier gelisteten Hosts bedingungslos akzeptiert. Diese Hosts werden dann nicht mit DNSBLs abgeglichen oder Greylisting unterworfen. Von ihnen empfangener Spam wird nie abgelehnt, optional kann er aber in den Spam-Ordner einsortiert werden. Die übliche Verwendung für diese Funktion ist, um Mailserver anzugeben, auf denen eine Weiterleitung zu Ihrem mailcow-Server eingerichtet wurde.';
$lang['admin']['forwarding_hosts_add_hint'] = 'Sie können entweder IPv4/IPv6-Adressen, Netzwerke in CIDR-Notation, Hostnamen (die zu IP-Adressen aufgelöst werden), oder Domainnamen (die zu IP-Adressen aufgelöst werden, indem ihr SPF-Record abgefragt wird oder, in dessen Abwesenheit, ihre MX-Records) angeben.';
$lang['admin']['relayhosts_hint'] = 'Erstellen Sie Relayhosts, um diese im Einstellungsdialog einer Domain auszuwählen.';
$lang['admin']['add_relayhost_add_hint'] = 'Bitte beachten Sie, dass Relayhost Anmeldedaten im Klartext gespeichert werden.';
$lang['admin']['host'] = 'Host';
$lang['admin']['source'] = 'Quelle';
$lang['admin']['add_forwarding_host'] = 'Weiterleitungs-Host hinzufügen';
$lang['admin']['add_relayhost'] = 'Relayhost hinzufügen';
$lang['delete']['remove_forwardinghost_warning'] = '<b>Warnung:</b> Sie entfernen den Weiterleitungs-Host <b>%s</b>!';
$lang['success']['forwarding_host_removed'] = "Weiterleitungs-Host %s wurde entfernt";
$lang['success']['forwarding_host_added'] = "Weiterleitungs-Host %s wurde hinzugefügt";
$lang['success']['relayhost_removed'] = "Relayhost %s wurde entfernt";
$lang['success']['relayhost_added'] = "Relayhost %s wurde hinzugefügt";

View File

@ -498,13 +498,20 @@ $lang['admin']['time'] = 'Time';
$lang['admin']['priority'] = 'Priority';
$lang['admin']['message'] = 'Message';
$lang['admin']['refresh'] = 'Refresh';
$lang['admin']['to_top'] = 'Back to top';
$lang['admin']['in_use_by'] = 'In use by';
$lang['admin']['logs'] = 'Logs';
$lang['admin']['forwarding_hosts'] = 'Forwarding Hosts';
$lang['admin']['forwarding_hosts_hint'] = 'Incoming messages are unconditionally accepted from any hosts listed here. These hosts are then not checked against DNSBLs or subjected to greylisting. Spam received from them is never rejected, but optionally it can be filed into the Junk folder. The most common use for this is to specify mail servers on which you have set up a rule that forwards incoming emails to your mailcow server.';
$lang['admin']['forwarding_hosts_add_hint'] = 'You can either specify IPv4/IPv6 addresses, networks in CIDR notation, host names (which will be resolved to IP addresses), or domain names (which will be resolved to IP addresses by querying SPF records or, in their absence, MX records).';
$lang['admin']['relayhosts_hint'] = 'Define relayhosts here to be able to select them in a domains configuration dialog.';
$lang['admin']['add_relayhost_add_hint'] = 'Please be aware that relayhost authentication data will be stored as plain text.';
$lang['admin']['host'] = 'Host';
$lang['admin']['source'] = 'Source';
$lang['admin']['add_forwarding_host'] = 'Add Forwarding Host';
$lang['admin']['add_relayhost'] = 'Add Relayhost';
$lang['delete']['remove_forwardinghost_warning'] = '<b>Warning:</b> You are about to remove the forwarding host <b>%s</b>!';
$lang['success']['forwarding_host_removed'] = "Forwarding host %s has been removed";
$lang['success']['forwarding_host_added'] = "Forwarding host %s has been added";
$lang['success']['relayhost_removed'] = "Relayhost %s has been removed";
$lang['success']['relayhost_added'] = "Relayhost %s has been added";

View File

@ -201,7 +201,7 @@ services:
- dovecot
postfix-mailcow:
image: mailcow/postfix:1.2
image: mailcow/postfix:1.3
build: ./data/Dockerfiles/postfix
depends_on:
unbound-mailcow: