1
0
mirror of https://github.com/mailcow/mailcow-dockerized.git synced 2025-12-25 07:41:30 +00:00

[Docker API] Use TLS encryption for communication with "on-the-fly" created key paris (non-exposed)

[Docker API] Create pipe to pass Rspamd UI worker password
[Dovecot] Pull Spamassassin ruleset to be read by Rspamd (MANY THANKS to Peer Heinlein!)
[Dovecot] Garbage collector for deleted maildirs (set keep time via MAILDIR_GC_TIME which defaults to 1440 minutes)
[Web] Flush memcached after mailbox item changes, fixes #1808
[Web] Fix duplicate IDs, fixes #1792
[Compose] Use SQL sockets
[PHP-FPM] Update APCu and Redis libs
[Dovecot] Encrypt maildir with global key pair in crypt-vol-1 (BACKUP!), also fixes #1791
[Web] Fix deletion of spam aliases
[Helper] Add "crypt" to backup script
[Helper] Override file for external SQL socket (not supported!)
[Compose] New images for Rspamd, PHP-FPM, SOGo, Dovecot, Docker API, Watchdog, ACME, Postfix
This commit is contained in:
André
2018-09-29 22:01:23 +02:00
parent 96c985abad
commit 0fb43f4916
49 changed files with 11437 additions and 419 deletions

View File

@@ -59,7 +59,7 @@ $(document).ready(function() {
});
$('#u2f_status_auth').html('<p><span class="glyphicon glyphicon-refresh glyphicon-spin"></span> Initializing, please wait...</p>');
$('#ConfirmTFAModal').on('shown.bs.modal', function(){
$(this).find('#token').focus();
$(this).find('input[name=token]').focus();
// If U2F
if(document.getElementById("u2f_auth_data") !== null) {
$.ajax({
@@ -146,7 +146,7 @@ $(document).ready(function() {
});
// CSRF
$('<input type="hidden" value="<?= $_SESSION['CSRF']['TOKEN']; ?>">').attr('id', 'csrf_token').attr('name', 'csrf_token').appendTo('form');
$('<input type="hidden" value="<?= $_SESSION['CSRF']['TOKEN']; ?>">').attr('name', 'csrf_token').appendTo('form');
if (sessionStorage.scrollTop != "undefined") {
$(window).scrollTop(sessionStorage.scrollTop);
}

View File

@@ -2,10 +2,13 @@
function docker($action, $service_name = null, $attr1 = null, $attr2 = null, $extra_headers = null) {
global $DOCKER_TIMEOUT;
$curl = curl_init();
curl_setopt($curl, CURLOPT_HTTPHEADER,array( 'Content-Type: application/json' ));
curl_setopt($curl, CURLOPT_HTTPHEADER,array('Content-Type: application/json' ));
// We are using our mail certificates for dockerapi, the names will not match, the certs are trusted anyway
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
switch($action) {
case 'get_id':
curl_setopt($curl, CURLOPT_URL, 'http://dockerapi:8080/containers/json');
curl_setopt($curl, CURLOPT_URL, 'https://dockerapi:443/containers/json');
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_POST, 0);
curl_setopt($curl, CURLOPT_TIMEOUT, $DOCKER_TIMEOUT);
@@ -38,7 +41,7 @@ function docker($action, $service_name = null, $attr1 = null, $attr2 = null, $ex
}
return false;
case 'containers':
curl_setopt($curl, CURLOPT_URL, 'http://dockerapi:8080/containers/json');
curl_setopt($curl, CURLOPT_URL, 'https://dockerapi:443/containers/json');
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_POST, 0);
curl_setopt($curl, CURLOPT_TIMEOUT, $DOCKER_TIMEOUT);
@@ -74,7 +77,7 @@ function docker($action, $service_name = null, $attr1 = null, $attr2 = null, $ex
break;
case 'info':
if (empty($service_name)) {
curl_setopt($curl, CURLOPT_URL, 'http://dockerapi:8080/containers/json');
curl_setopt($curl, CURLOPT_URL, 'https://dockerapi:443/containers/json');
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_POST, 0);
curl_setopt($curl, CURLOPT_TIMEOUT, $DOCKER_TIMEOUT);
@@ -82,7 +85,7 @@ function docker($action, $service_name = null, $attr1 = null, $attr2 = null, $ex
else {
$container_id = docker('get_id', $service_name);
if (ctype_xdigit($container_id)) {
curl_setopt($curl, CURLOPT_URL, 'http://dockerapi:8080/containers/' . $container_id . '/json');
curl_setopt($curl, CURLOPT_URL, 'https://dockerapi:443/containers/' . $container_id . '/json');
}
else {
// logger(array('return' => array(
@@ -144,7 +147,7 @@ function docker($action, $service_name = null, $attr1 = null, $attr2 = null, $ex
if (!empty($attr1)) {
$container_id = docker('get_id', $service_name);
if (ctype_xdigit($container_id) && ctype_alnum($attr1)) {
curl_setopt($curl, CURLOPT_URL, 'http://dockerapi:8080/containers/' . $container_id . '/' . $attr1);
curl_setopt($curl, CURLOPT_URL, 'https://dockerapi:443/containers/' . $container_id . '/' . $attr1);
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_TIMEOUT, $DOCKER_TIMEOUT);
if (!empty($attr2)) {
@@ -158,19 +161,19 @@ function docker($action, $service_name = null, $attr1 = null, $attr2 = null, $ex
if ($response === false) {
$err = curl_error($curl);
curl_close($curl);
logger(array('return' => array(array(
'type' => 'danger',
'log' => array(__FUNCTION__, $action, $service_name, $attr1, $attr2, $extra_headers),
'msg' => $err,
))));
// logger(array('return' => array(array(
// 'type' => 'danger',
// 'log' => array(__FUNCTION__, $action, $service_name, $attr1, $attr2, $extra_headers),
// 'msg' => $err,
// ))));
return $err;
}
else {
curl_close($curl);
logger(array('return' => array(array(
'type' => 'success',
'log' => array(__FUNCTION__, $action, $service_name, $attr1, $attr2, $extra_headers),
))));
// logger(array('return' => array(array(
// 'type' => 'success',
// 'log' => array(__FUNCTION__, $action, $service_name, $attr1, $attr2, $extra_headers),
// ))));
if (empty($response)) {
return true;
}

View File

@@ -18,6 +18,17 @@ function last_login($user) {
return false;
}
}
function flush_memcached() {
try {
$m = new Memcached();
$m->addServer('memcached', 11211);
$m->flush();
}
catch ( Exception $e ) {
// Dunno
}
}
function logger($_data = false) {
/*
logger() will be called as last function
@@ -510,6 +521,7 @@ function update_sogo_static_view() {
$stmt = $pdo->query("REPLACE INTO _sogo_static_view SELECT * from sogo_view");
$stmt = $pdo->query("DELETE FROM _sogo_static_view WHERE `c_uid` NOT IN (SELECT `username` FROM `mailbox` WHERE `active` = '1');");
}
flush_memcached();
}
function edit_user_account($_data) {
global $lang;

View File

@@ -2776,6 +2776,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
`mailbox`.`active` AS `active_int`,
CASE `mailbox`.`active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `active`,
`mailbox`.`domain`,
`mailbox`.`maildir`,
`mailbox`.`quota`,
`quota2`.`bytes`,
`attributes`,
@@ -2806,6 +2807,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$mailboxdata['active'] = $row['active'];
$mailboxdata['active_int'] = $row['active_int'];
$mailboxdata['domain'] = $row['domain'];
$mailboxdata['maildir'] = $row['maildir'];
$mailboxdata['quota'] = $row['quota'];
$mailboxdata['attributes'] = json_decode($row['attributes'], true);
$mailboxdata['quota_used'] = intval($row['bytes']);
@@ -3054,6 +3056,16 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
);
continue;
}
$exec_fields = array('cmd' => 'maildir_cleanup', 'maildir' => $domain);
$maildir_gc = json_decode(docker('post', 'dovecot-mailcow', 'exec', $exec_fields), true);
if ($maildir_gc['type'] != 'success') {
$_SESSION['return'][] = array(
'type' => 'warning',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => 'Could not move maildir to garbage collector: ' . $maildir_gc['msg']
);
}
return false;
$stmt = $pdo->prepare("DELETE FROM `domain` WHERE `domain` = :domain");
$stmt->execute(array(
':domain' => $domain,
@@ -3078,17 +3090,17 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$stmt->execute(array(
':domain' => '%@'.$domain,
));
$stmt = $pdo->prepare("DELETE FROM `quota2` WHERE `username` = :domain");
$stmt = $pdo->prepare("DELETE FROM `quota2` WHERE `username` LIKE :domain");
$stmt->execute(array(
':domain' => '%@'.$domain,
));
$stmt = $pdo->prepare("DELETE FROM `spamalias` WHERE `address` = :domain");
$stmt = $pdo->prepare("DELETE FROM `spamalias` WHERE `address` LIKE :domain");
$stmt->execute(array(
':domain' => '%@'.$domain,
));
$stmt = $pdo->prepare("DELETE FROM `filterconf` WHERE `object` = :domain");
$stmt->execute(array(
':domain' => '%@'.$domain,
':domain' => $domain,
));
$stmt = $pdo->prepare("DELETE FROM `bcc_maps` WHERE `local_dest` = :domain");
$stmt->execute(array(
@@ -3227,6 +3239,16 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
);
continue;
}
$maildir = mailbox('get', 'mailbox_details', $username)['maildir'];
$exec_fields = array('cmd' => 'maildir_cleanup', 'maildir' => $maildir);
$maildir_gc = json_decode(docker('post', 'dovecot-mailcow', 'exec', $exec_fields), true);
if ($maildir_gc['type'] != 'success') {
$_SESSION['return'][] = array(
'type' => 'warning',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => 'Could not move maildir to garbage collector: ' . $maildir_gc['msg']
);
}
$stmt = $pdo->prepare("DELETE FROM `alias` WHERE `goto` = :username");
$stmt->execute(array(
':username' => $username

View File

@@ -3,7 +3,7 @@ function init_db_schema() {
try {
global $pdo;
$db_version = "01092018_1902";
$db_version = "21092018_1902";
$stmt = $pdo->query("SHOW TABLES LIKE 'versions'");
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));

View File

@@ -35,7 +35,7 @@ $hrs = floor($mins / 60);
$mins -= $hrs * 60;
$offset = sprintf('%+d:%02d', $hrs*$sgn, $mins);
$dsn = $database_type . ":host=" . $database_host . ";dbname=" . $database_name;
$dsn = $database_type . ":unix_socket=" . $database_sock . ";dbname=" . $database_name;
$opt = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
@@ -46,11 +46,20 @@ try {
$pdo = new PDO($dsn, $database_user, $database_pass, $opt);
}
catch (PDOException $e) {
// Stop when SQL connection fails
?>
<center style='font-family: "Lucida Sans Unicode", "Lucida Grande", Verdana, Arial, Helvetica, sans-serif;'>Connection to database failed.<br /><br />The following error was reported:<br/> <?=$e->getMessage();?></center>
<center style='font-family:sans-serif;'>Connection to database failed.<br /><br />The following error was reported:<br/> <?=$e->getMessage();?></center>
<?php
exit;
}
// Stop when dockerapi is not available
if (fsockopen("tcp://dockerapi", 443, $errno, $errstr) === false) {
?>
<center style='font-family:sans-serif;'>Connection to dockerapi container failed.<br /><br />The following error was reported:<br/><?=$errno;?> - <?=$errstr;?></center>
<?php
exit;
}
function pdo_exception_handler($e) {
print_r($e);
if ($e instanceof PDOException) {
@@ -146,3 +155,4 @@ if (isset($_SESSION['mailcow_cc_role'])) {
acl('to_session');
}
$UI_TEXTS = customize('get', 'ui_texts');

View File

@@ -9,6 +9,7 @@ This file will be reset on upgrades.
// SQL database connection variables
$database_type = 'mysql';
$database_sock = '/var/run/mysqld/mysqld.sock';
$database_host = 'mysql';
$database_user = getenv('DBUSER');
$database_pass = getenv('DBPASS');