mirror of
https://github.com/mailcow/mailcow-dockerized.git
synced 2026-10-26 11:13:24 +00:00
[Web] add mTLS Authentication
This commit is contained in:
15 files changed
+303
-62
No files matched your search
@@ -204,6 +204,17 @@ chown -R 82:82 /web/templates/cache
|
|||||||
# Clear cache
|
# Clear cache
|
||||||
find /web/templates/cache/* -not -name '.gitkeep' -delete
|
find /web/templates/cache/* -not -name '.gitkeep' -delete
|
||||||
|
|
||||||
|
# list client ca of all domains for
|
||||||
|
CA_LIST="/etc/nginx/conf.d/client_cas.crt"
|
||||||
|
# Clear the output file
|
||||||
|
> "$CA_LIST"
|
||||||
|
# Execute the query and append each value to the output file
|
||||||
|
mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT ssl_client_ca FROM domain;" | while read -r ca; do
|
||||||
|
echo "$ca" >> "$CA_LIST"
|
||||||
|
done
|
||||||
|
echo "SSL client CAs have been appended to $CA_LIST"
|
||||||
|
|
||||||
|
|
||||||
# Run hooks
|
# Run hooks
|
||||||
for file in /hooks/*; do
|
for file in /hooks/*; do
|
||||||
if [ -x "${file}" ]; then
|
if [ -x "${file}" ]; then
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
ssl_session_timeout 1d;
|
ssl_session_timeout 1d;
|
||||||
ssl_session_tickets off;
|
ssl_session_tickets off;
|
||||||
|
|
||||||
|
include /etc/nginx/conf.d/includes/ssl_client_auth.conf;
|
||||||
|
|
||||||
add_header Strict-Transport-Security "max-age=15768000;";
|
add_header Strict-Transport-Security "max-age=15768000;";
|
||||||
add_header X-Content-Type-Options nosniff;
|
add_header X-Content-Type-Options nosniff;
|
||||||
add_header X-XSS-Protection "1; mode=block";
|
add_header X-XSS-Protection "1; mode=block";
|
||||||
@@ -101,6 +103,10 @@
|
|||||||
include /etc/nginx/fastcgi_params;
|
include /etc/nginx/fastcgi_params;
|
||||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||||
fastcgi_param PATH_INFO $fastcgi_path_info;
|
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||||
|
fastcgi_param TLS_SUCCESS $ssl_client_verify;
|
||||||
|
fastcgi_param TLS_ISSUER $ssl_client_i_dn;
|
||||||
|
fastcgi_param TLS_DN $ssl_client_s_dn;
|
||||||
|
fastcgi_param TLS_CERT $ssl_client_cert;
|
||||||
fastcgi_read_timeout 3600;
|
fastcgi_read_timeout 3600;
|
||||||
fastcgi_send_timeout 3600;
|
fastcgi_send_timeout 3600;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
ssl_verify_client optional;
|
||||||
|
ssl_client_certificate /etc/nginx/conf.d/client_cas.crt;
|
||||||
|
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
apk add mariadb-client
|
||||||
|
|
||||||
|
# List client CA of all domains
|
||||||
|
CA_LIST="/etc/nginx/conf.d/client_cas.crt"
|
||||||
|
> "$CA_LIST"
|
||||||
|
|
||||||
|
# Define your SQL query
|
||||||
|
query="SELECT DISTINCT ssl_client_ca FROM domain WHERE ssl_client_ca IS NOT NULL;"
|
||||||
|
result=$(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "$query" -B -N)
|
||||||
|
if [ -n "$result" ]; then
|
||||||
|
echo "$result" | while IFS= read -r line; do
|
||||||
|
echo -e "$line"
|
||||||
|
done > $CA_LIST
|
||||||
|
#tail -n 1 "$CA_LIST" | wc -c | xargs -I {} truncate "$CA_LIST" -s -{}
|
||||||
|
echo "
|
||||||
|
ssl_verify_client optional;
|
||||||
|
ssl_client_certificate /etc/nginx/conf.d/client_cas.crt;
|
||||||
|
" > /etc/nginx/conf.d/includes/ssl_client_auth.conf
|
||||||
|
echo "SSL client CAs have been appended to $CA_LIST"
|
||||||
|
else
|
||||||
|
> /etc/nginx/conf.d/includes/ssl_client_auth.conf
|
||||||
|
echo "No SSL client CAs found"
|
||||||
|
fi
|
||||||
@@ -233,6 +233,75 @@ function user_login($user, $pass, $extra = null){
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
function user_mutualtls_login() {
|
||||||
|
global $pdo;
|
||||||
|
|
||||||
|
if (empty($_SERVER["TLS_SUCCESS"]) || empty($_SERVER["TLS_DN"]) || empty($_SERVER["TLS_ISSUER"])) {
|
||||||
|
// missing info
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!$_SERVER["TLS_SUCCESS"]) {
|
||||||
|
// mutual tls login failed
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse dn
|
||||||
|
$pairs = explode(',', $_SERVER["TLS_DN"]);
|
||||||
|
$dn_details = [];
|
||||||
|
foreach ($pairs as $pair) {
|
||||||
|
$keyValue = explode('=', $pair);
|
||||||
|
$dn_details[$keyValue[0]] = $keyValue[1];
|
||||||
|
}
|
||||||
|
// parse dn
|
||||||
|
$pairs = explode(',', $_SERVER["TLS_ISSUER"]);
|
||||||
|
$issuer_details = [];
|
||||||
|
foreach ($pairs as $pair) {
|
||||||
|
$keyValue = explode('=', $pair);
|
||||||
|
$issuer_details[$keyValue[0]] = $keyValue[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = $dn_details['emailAddress'];
|
||||||
|
if (empty($user)){
|
||||||
|
// no user specified
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$search = "";
|
||||||
|
ksort($issuer_details);
|
||||||
|
foreach ($issuer_details as $key => $value) {
|
||||||
|
$search .= "{$key}={$value},";
|
||||||
|
}
|
||||||
|
$search = rtrim($search, ',');
|
||||||
|
if (empty($search)){
|
||||||
|
// incomplete issuer details
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$user_split = explode('@', $user);
|
||||||
|
$local_part = $user_split[0];
|
||||||
|
$domain = $user_split[1];
|
||||||
|
// search for match
|
||||||
|
$stmt = $pdo->prepare("SELECT * FROM `domain` AS d1
|
||||||
|
INNER JOIN `mailbox` ON mailbox.domain = d1.domain
|
||||||
|
INNER JOIN `domain` AS d2 ON mailbox.domain = d2.domain
|
||||||
|
WHERE `kind` NOT REGEXP 'location|thing|group'
|
||||||
|
AND d2.`ssl_client_issuer` = :search
|
||||||
|
AND d2.`active`='1'
|
||||||
|
AND mailbox.`active`='1'
|
||||||
|
AND mailbox.`username` = :user");
|
||||||
|
$stmt->execute(array(
|
||||||
|
':search' => $search,
|
||||||
|
':user' => $user
|
||||||
|
));
|
||||||
|
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
// user not found
|
||||||
|
if (!$row){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
function apppass_login($user, $pass, $app_passwd_data, $extra = null){
|
function apppass_login($user, $pass, $app_passwd_data, $extra = null){
|
||||||
global $pdo;
|
global $pdo;
|
||||||
|
|
||||||
|
|||||||
@@ -2522,6 +2522,31 @@ function clear_session(){
|
|||||||
session_destroy();
|
session_destroy();
|
||||||
session_write_close();
|
session_write_close();
|
||||||
}
|
}
|
||||||
|
function is_valid_ssl_cert($cert) {
|
||||||
|
if (empty($cert)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$cert_res = openssl_x509_read($cert);
|
||||||
|
if ($cert_res === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
openssl_x509_free($cert_res);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
function has_ssl_client_auth() {
|
||||||
|
global $pdo;
|
||||||
|
|
||||||
|
$stmt = $pdo->query("SELECT domain FROM `domain`
|
||||||
|
WHERE `ssl_client_ca` IS NOT NULL
|
||||||
|
AND `ssl_client_issuer` IS NOT NULL");
|
||||||
|
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
if (!$row){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
function get_logs($application, $lines = false) {
|
function get_logs($application, $lines = false) {
|
||||||
if ($lines === false) {
|
if ($lines === false) {
|
||||||
|
|||||||
@@ -528,11 +528,24 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$active = (isset($_data['active'])) ? intval($_data['active']) : $DOMAIN_DEFAULT_ATTRIBUTES['active'];
|
$active = (isset($_data['active'])) ? intval($_data['active']) : $DOMAIN_DEFAULT_ATTRIBUTES['active'];
|
||||||
$relay_all_recipients = (isset($_data['relay_all_recipients'])) ? intval($_data['relay_all_recipients']) : $DOMAIN_DEFAULT_ATTRIBUTES['relay_all_recipients'];
|
$relay_all_recipients = (isset($_data['relay_all_recipients'])) ? intval($_data['relay_all_recipients']) : $DOMAIN_DEFAULT_ATTRIBUTES['relay_all_recipients'];
|
||||||
$relay_unknown_only = (isset($_data['relay_unknown_only'])) ? intval($_data['relay_unknown_only']) : $DOMAIN_DEFAULT_ATTRIBUTES['relay_unknown_only'];
|
$relay_unknown_only = (isset($_data['relay_unknown_only'])) ? intval($_data['relay_unknown_only']) : $DOMAIN_DEFAULT_ATTRIBUTES['relay_unknown_only'];
|
||||||
$backupmx = (isset($_data['backupmx'])) ? intval($_data['backupmx']) : $DOMAIN_DEFAULT_ATTRIBUTES['backupmx'];
|
$backupmx = (isset($_data['backupmx'])) ? intval($_data['backupmx']) : $DOMAIN_DEFAULT_ATTRIBUTES['backupmx'];
|
||||||
$gal = (isset($_data['gal'])) ? intval($_data['gal']) : $DOMAIN_DEFAULT_ATTRIBUTES['gal'];
|
$gal = (isset($_data['gal'])) ? intval($_data['gal']) : $DOMAIN_DEFAULT_ATTRIBUTES['gal'];
|
||||||
|
$ssl_client_ca = (is_valid_ssl_cert(trim($_data['ssl_client_ca']))) ? trim($_data['ssl_client_ca']) : null;
|
||||||
|
$ssl_client_issuer = "";
|
||||||
|
if (isset($ssl_client_ca)) {
|
||||||
|
$ca_issuer = openssl_x509_parse($ssl_client_ca);
|
||||||
|
if (!empty($ca_issuer) && is_array($ca_issuer['issuer'])){
|
||||||
|
$ca_issuer = $ca_issuer['issuer'];
|
||||||
|
ksort($ca_issuer);
|
||||||
|
foreach ($ca_issuer as $key => $value) {
|
||||||
|
$ssl_client_issuer .= "{$key}={$value},";
|
||||||
|
}
|
||||||
|
$ssl_client_issuer = rtrim($ssl_client_issuer, ',');
|
||||||
|
}
|
||||||
|
}
|
||||||
if ($relay_all_recipients == 1) {
|
if ($relay_all_recipients == 1) {
|
||||||
$backupmx = '1';
|
$backupmx = '1';
|
||||||
}
|
}
|
||||||
@@ -588,22 +601,33 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
':domain' => '%@' . $domain
|
':domain' => '%@' . $domain
|
||||||
));
|
));
|
||||||
// save domain
|
// save domain
|
||||||
$stmt = $pdo->prepare("INSERT INTO `domain` (`domain`, `description`, `aliases`, `mailboxes`, `defquota`, `maxquota`, `quota`, `backupmx`, `gal`, `active`, `relay_unknown_only`, `relay_all_recipients`)
|
try {
|
||||||
VALUES (:domain, :description, :aliases, :mailboxes, :defquota, :maxquota, :quota, :backupmx, :gal, :active, :relay_unknown_only, :relay_all_recipients)");
|
$stmt = $pdo->prepare("INSERT INTO `domain` (`domain`, `description`, `aliases`, `mailboxes`, `defquota`, `maxquota`, `quota`, `backupmx`, `gal`, `active`, `relay_unknown_only`, `relay_all_recipients`, `ssl_client_issuer`, `ssl_client_ca`)
|
||||||
$stmt->execute(array(
|
VALUES (:domain, :description, :aliases, :mailboxes, :defquota, :maxquota, :quota, :backupmx, :gal, :active, :relay_unknown_only, :relay_all_recipients, :ssl_client_issuer, :ssl_client_ca)");
|
||||||
':domain' => $domain,
|
$stmt->execute(array(
|
||||||
':description' => $description,
|
':domain' => $domain,
|
||||||
':aliases' => $aliases,
|
':description' => $description,
|
||||||
':mailboxes' => $mailboxes,
|
':aliases' => $aliases,
|
||||||
':defquota' => $defquota,
|
':mailboxes' => $mailboxes,
|
||||||
':maxquota' => $maxquota,
|
':defquota' => $defquota,
|
||||||
':quota' => $quota,
|
':maxquota' => $maxquota,
|
||||||
':backupmx' => $backupmx,
|
':quota' => $quota,
|
||||||
':gal' => $gal,
|
':backupmx' => $backupmx,
|
||||||
':active' => $active,
|
':gal' => $gal,
|
||||||
':relay_unknown_only' => $relay_unknown_only,
|
':active' => $active,
|
||||||
':relay_all_recipients' => $relay_all_recipients
|
':relay_unknown_only' => $relay_unknown_only,
|
||||||
));
|
':relay_all_recipients' => $relay_all_recipients,
|
||||||
|
':ssl_client_issuer' => $ssl_client_issuer,
|
||||||
|
'ssl_client_ca' => $ssl_client_ca
|
||||||
|
));
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||||
|
'msg' => $e->getMessage()
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
// save tags
|
// save tags
|
||||||
foreach($tags as $index => $tag){
|
foreach($tags as $index => $tag){
|
||||||
if (empty($tag)) continue;
|
if (empty($tag)) continue;
|
||||||
@@ -654,15 +678,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
}
|
}
|
||||||
if (!empty($restart_sogo)) {
|
if (!empty($restart_sogo)) {
|
||||||
$restart_response = json_decode(docker('post', 'sogo-mailcow', 'restart'), true);
|
$restart_response = json_decode(docker('post', 'sogo-mailcow', 'restart'), true);
|
||||||
if ($restart_response['type'] == "success") {
|
if ($restart_response['type'] != "success") {
|
||||||
$_SESSION['return'][] = array(
|
|
||||||
'type' => 'success',
|
|
||||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
|
||||||
'msg' => array('domain_added', htmlspecialchars($domain))
|
|
||||||
);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'warning',
|
'type' => 'warning',
|
||||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||||
@@ -671,6 +687,18 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!empty($ssl_client_ca) && !empty($ssl_client_issuer)) {
|
||||||
|
// restart nginx
|
||||||
|
$restart_response = json_decode(docker('post', 'nginx-mailcow', 'restart'), true);
|
||||||
|
if ($restart_response['type'] != "success") {
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'warning',
|
||||||
|
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||||
|
'msg' => 'nginx_restart_failed'
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'success',
|
'type' => 'success',
|
||||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||||
@@ -2674,6 +2702,21 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
$quota = (!empty($_data['quota'])) ? $_data['quota'] : ($is_now['max_quota_for_domain'] / 1048576);
|
$quota = (!empty($_data['quota'])) ? $_data['quota'] : ($is_now['max_quota_for_domain'] / 1048576);
|
||||||
$description = (!empty($_data['description'])) ? $_data['description'] : $is_now['description'];
|
$description = (!empty($_data['description'])) ? $_data['description'] : $is_now['description'];
|
||||||
$tags = (is_array($_data['tags']) ? $_data['tags'] : array());
|
$tags = (is_array($_data['tags']) ? $_data['tags'] : array());
|
||||||
|
$ssl_client_ca = (is_valid_ssl_cert(trim($_data['ssl_client_ca']))) ? trim($_data['ssl_client_ca']) : $is_now['ssl_client_ca'];
|
||||||
|
$ssl_client_issuer = $is_now['ssl_client_issuer'];
|
||||||
|
if (is_valid_ssl_cert(trim($_data['ssl_client_ca']))){
|
||||||
|
if (isset($ssl_client_ca)) {
|
||||||
|
$ca_issuer = openssl_x509_parse($ssl_client_ca);
|
||||||
|
if (!empty($ca_issuer) && is_array($ca_issuer['issuer'])){
|
||||||
|
$ca_issuer = $ca_issuer['issuer'];
|
||||||
|
ksort($ca_issuer);
|
||||||
|
foreach ($ca_issuer as $key => $value) {
|
||||||
|
$ssl_client_issuer .= "{$key}={$value},";
|
||||||
|
}
|
||||||
|
$ssl_client_issuer = rtrim($ssl_client_issuer, ',');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if ($relay_all_recipients == '1') {
|
if ($relay_all_recipients == '1') {
|
||||||
$backupmx = '1';
|
$backupmx = '1';
|
||||||
}
|
}
|
||||||
@@ -2773,35 +2816,47 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$stmt = $pdo->prepare("UPDATE `domain` SET
|
try {
|
||||||
`relay_all_recipients` = :relay_all_recipients,
|
$stmt = $pdo->prepare("UPDATE `domain` SET
|
||||||
`relay_unknown_only` = :relay_unknown_only,
|
`relay_all_recipients` = :relay_all_recipients,
|
||||||
`backupmx` = :backupmx,
|
`relay_unknown_only` = :relay_unknown_only,
|
||||||
`gal` = :gal,
|
`backupmx` = :backupmx,
|
||||||
`active` = :active,
|
`gal` = :gal,
|
||||||
`quota` = :quota,
|
`active` = :active,
|
||||||
`defquota` = :defquota,
|
`quota` = :quota,
|
||||||
`maxquota` = :maxquota,
|
`defquota` = :defquota,
|
||||||
`relayhost` = :relayhost,
|
`maxquota` = :maxquota,
|
||||||
`mailboxes` = :mailboxes,
|
`relayhost` = :relayhost,
|
||||||
`aliases` = :aliases,
|
`mailboxes` = :mailboxes,
|
||||||
`description` = :description
|
`aliases` = :aliases,
|
||||||
WHERE `domain` = :domain");
|
`description` = :description,
|
||||||
$stmt->execute(array(
|
`ssl_client_ca` = :ssl_client_ca,
|
||||||
':relay_all_recipients' => $relay_all_recipients,
|
`ssl_client_issuer` = :ssl_client_issuer
|
||||||
':relay_unknown_only' => $relay_unknown_only,
|
WHERE `domain` = :domain");
|
||||||
':backupmx' => $backupmx,
|
$stmt->execute(array(
|
||||||
':gal' => $gal,
|
':relay_all_recipients' => $relay_all_recipients,
|
||||||
':active' => $active,
|
':relay_unknown_only' => $relay_unknown_only,
|
||||||
':quota' => $quota,
|
':backupmx' => $backupmx,
|
||||||
':defquota' => $defquota,
|
':gal' => $gal,
|
||||||
':maxquota' => $maxquota,
|
':active' => $active,
|
||||||
':relayhost' => $relayhost,
|
':quota' => $quota,
|
||||||
':mailboxes' => $mailboxes,
|
':defquota' => $defquota,
|
||||||
':aliases' => $aliases,
|
':maxquota' => $maxquota,
|
||||||
':description' => $description,
|
':relayhost' => $relayhost,
|
||||||
':domain' => $domain
|
':mailboxes' => $mailboxes,
|
||||||
));
|
':aliases' => $aliases,
|
||||||
|
':description' => $description,
|
||||||
|
':ssl_client_ca' => $ssl_client_ca,
|
||||||
|
':ssl_client_issuer' => $ssl_client_issuer,
|
||||||
|
':domain' => $domain
|
||||||
|
));
|
||||||
|
}catch (PDOException $e) {
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||||
|
'msg' => $e->getMessage()
|
||||||
|
);
|
||||||
|
}
|
||||||
// save tags
|
// save tags
|
||||||
foreach($tags as $index => $tag){
|
foreach($tags as $index => $tag){
|
||||||
if (empty($tag)) continue;
|
if (empty($tag)) continue;
|
||||||
@@ -4416,7 +4471,8 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
`relay_unknown_only`,
|
`relay_unknown_only`,
|
||||||
`backupmx`,
|
`backupmx`,
|
||||||
`gal`,
|
`gal`,
|
||||||
`active`
|
`active`,
|
||||||
|
`ssl_client_ca`
|
||||||
FROM `domain` WHERE `domain`= :domain");
|
FROM `domain` WHERE `domain`= :domain");
|
||||||
$stmt->execute(array(
|
$stmt->execute(array(
|
||||||
':domain' => $_data
|
':domain' => $_data
|
||||||
@@ -4484,6 +4540,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
$domaindata['relay_unknown_only_int'] = $row['relay_unknown_only'];
|
$domaindata['relay_unknown_only_int'] = $row['relay_unknown_only'];
|
||||||
$domaindata['created'] = $row['created'];
|
$domaindata['created'] = $row['created'];
|
||||||
$domaindata['modified'] = $row['modified'];
|
$domaindata['modified'] = $row['modified'];
|
||||||
|
$domaindata['ssl_client_ca'] = $row['ssl_client_ca'];
|
||||||
$stmt = $pdo->prepare("SELECT COUNT(`address`) AS `alias_count` FROM `alias`
|
$stmt = $pdo->prepare("SELECT COUNT(`address`) AS `alias_count` FROM `alias`
|
||||||
WHERE (`domain`= :domain OR `domain` IN (SELECT `alias_domain` FROM `alias_domain` WHERE `target_domain` = :domain2))
|
WHERE (`domain`= :domain OR `domain` IN (SELECT `alias_domain` FROM `alias_domain` WHERE `target_domain` = :domain2))
|
||||||
AND `address` NOT IN (
|
AND `address` NOT IN (
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ function init_db_schema() {
|
|||||||
try {
|
try {
|
||||||
global $pdo;
|
global $pdo;
|
||||||
|
|
||||||
$db_version = "08012024_1442";
|
$db_version = "08022024_1302";
|
||||||
|
|
||||||
$stmt = $pdo->query("SHOW TABLES LIKE 'versions'");
|
$stmt = $pdo->query("SHOW TABLES LIKE 'versions'");
|
||||||
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
||||||
@@ -256,6 +256,8 @@ function init_db_schema() {
|
|||||||
"gal" => "TINYINT(1) NOT NULL DEFAULT '1'",
|
"gal" => "TINYINT(1) NOT NULL DEFAULT '1'",
|
||||||
"relay_all_recipients" => "TINYINT(1) NOT NULL DEFAULT '0'",
|
"relay_all_recipients" => "TINYINT(1) NOT NULL DEFAULT '0'",
|
||||||
"relay_unknown_only" => "TINYINT(1) NOT NULL DEFAULT '0'",
|
"relay_unknown_only" => "TINYINT(1) NOT NULL DEFAULT '0'",
|
||||||
|
"ssl_client_issuer" => "TEXT",
|
||||||
|
"ssl_client_ca" => "TEXT",
|
||||||
"created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)",
|
"created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)",
|
||||||
"modified" => "DATETIME ON UPDATE CURRENT_TIMESTAMP",
|
"modified" => "DATETIME ON UPDATE CURRENT_TIMESTAMP",
|
||||||
"active" => "TINYINT(1) NOT NULL DEFAULT '1'"
|
"active" => "TINYINT(1) NOT NULL DEFAULT '1'"
|
||||||
|
|||||||
@@ -26,6 +26,26 @@ if ($iam_provider){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isset($_GET['mutual_tls_login'])) {
|
||||||
|
$mutual_login_user = user_mutualtls_login();
|
||||||
|
if ($mutual_login_user != false) {
|
||||||
|
$_SESSION['mailcow_cc_username'] = $mutual_login_user;
|
||||||
|
$_SESSION['mailcow_cc_role'] = "user";
|
||||||
|
|
||||||
|
$http_parameters = explode('&', $_SESSION['index_query_string']);
|
||||||
|
unset($_SESSION['index_query_string']);
|
||||||
|
if (in_array('mobileconfig', $http_parameters)) {
|
||||||
|
if (in_array('only_email', $http_parameters)) {
|
||||||
|
header("Location: /mobileconfig.php?only_email");
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
header("Location: /mobileconfig.php");
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
header("Location: /user");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// SSO Domain Admin
|
// SSO Domain Admin
|
||||||
if (!empty($_GET['sso_token'])) {
|
if (!empty($_GET['sso_token'])) {
|
||||||
$username = domain_admin_sso('check', $_GET['sso_token']);
|
$username = domain_admin_sso('check', $_GET['sso_token']);
|
||||||
|
|||||||
+2
-1
@@ -29,7 +29,8 @@ $template_data = [
|
|||||||
'oauth2_request' => @$_SESSION['oauth2_request'],
|
'oauth2_request' => @$_SESSION['oauth2_request'],
|
||||||
'is_mobileconfig' => str_contains($_SESSION['index_query_string'], 'mobileconfig'),
|
'is_mobileconfig' => str_contains($_SESSION['index_query_string'], 'mobileconfig'),
|
||||||
'login_delay' => @$_SESSION['ldelay'],
|
'login_delay' => @$_SESSION['ldelay'],
|
||||||
'has_iam_sso' => ($iam_provider) ? true : false
|
'has_iam_sso' => ($iam_provider) ? true : false,
|
||||||
|
'has_ssl_client_auth' => has_ssl_client_auth()
|
||||||
];
|
];
|
||||||
|
|
||||||
$js_minifier->add('/web/js/site/index.js');
|
$js_minifier->add('/web/js/site/index.js');
|
||||||
|
|||||||
@@ -345,6 +345,8 @@
|
|||||||
"service_id": "Service ID",
|
"service_id": "Service ID",
|
||||||
"source": "Source",
|
"source": "Source",
|
||||||
"spamfilter": "Spam filter",
|
"spamfilter": "Spam filter",
|
||||||
|
"ssl_client_auth": "mTLS",
|
||||||
|
"ssl_client_ca": "CA for mTLS Login",
|
||||||
"subject": "Subject",
|
"subject": "Subject",
|
||||||
"success": "Success",
|
"success": "Success",
|
||||||
"sys_mails": "System mails",
|
"sys_mails": "System mails",
|
||||||
|
|||||||
@@ -91,12 +91,18 @@
|
|||||||
<input type="number" class="form-control" name="maxquota" value="{{ (result.max_quota_for_mbox / 1048576) }}">
|
<input type="number" class="form-control" name="maxquota" value="{{ (result.max_quota_for_mbox / 1048576) }}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-4">
|
<div class="row mb-2">
|
||||||
<label class="control-label col-sm-2" for="quota">{{ lang.edit.domain_quota }}</label>
|
<label class="control-label col-sm-2" for="quota">{{ lang.edit.domain_quota }}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="number" class="form-control" name="quota" value="{{ (result.max_quota_for_domain / 1048576) }}">
|
<input type="number" class="form-control" name="quota" value="{{ (result.max_quota_for_domain / 1048576) }}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row mb-4">
|
||||||
|
<label class="control-label col-sm-2" for="quota">{{ lang.admin.ssl_client_ca }}</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<textarea class="form-control" id="ssl_client_ca" name="ssl_client_ca" style="height: 200px;">{{ result.ssl_client_ca }}</textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="row mb-2">
|
<div class="row mb-2">
|
||||||
<label class="control-label col-sm-2">{{ lang.edit.backup_mx_options }}</label>
|
<label class="control-label col-sm-2">{{ lang.edit.backup_mx_options }}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
|
|||||||
@@ -49,6 +49,9 @@
|
|||||||
{% if has_iam_sso %}
|
{% if has_iam_sso %}
|
||||||
<li><a class="dropdown-item" href="/?iam_sso=1"><i class="bi bi-cloud-arrow-up-fill"></i> {{ lang.admin.iam_sso }}</a></li>
|
<li><a class="dropdown-item" href="/?iam_sso=1"><i class="bi bi-cloud-arrow-up-fill"></i> {{ lang.admin.iam_sso }}</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if has_ssl_client_auth %}
|
||||||
|
<li><a class="dropdown-item" href="/?mutual_tls_login=1"><i class="bi bi-cloud-arrow-up-fill"></i> {{ lang.admin.ssl_client_auth }}</a></li>
|
||||||
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
{% if not oauth2_request %}
|
{% if not oauth2_request %}
|
||||||
|
|||||||
@@ -489,6 +489,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
|
<div class="row mb-4">
|
||||||
|
<label class="control-label col-sm-2 text-sm-end text-sm-end" for="ssl_client_ca">{{ lang.admin.ssl_client_ca }}</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<textarea class="form-control" id="ssl_client_ca" name="ssl_client_ca" style="height: 200px;"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
<div class="row mb-4">
|
<div class="row mb-4">
|
||||||
<label class="control-label col-sm-2 text-sm-end text-sm-end">{{ lang.add.backup_mx_options }}</label>
|
<label class="control-label col-sm-2 text-sm-end text-sm-end">{{ lang.add.backup_mx_options }}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
|
|||||||
@@ -380,6 +380,7 @@ services:
|
|||||||
. /etc/nginx/conf.d/templates/server_name.template.sh > /etc/nginx/conf.d/server_name.active &&
|
. /etc/nginx/conf.d/templates/server_name.template.sh > /etc/nginx/conf.d/server_name.active &&
|
||||||
. /etc/nginx/conf.d/templates/sites.template.sh > /etc/nginx/conf.d/sites.active &&
|
. /etc/nginx/conf.d/templates/sites.template.sh > /etc/nginx/conf.d/sites.active &&
|
||||||
. /etc/nginx/conf.d/templates/sogo_eas.template.sh > /etc/nginx/conf.d/sogo_eas.active &&
|
. /etc/nginx/conf.d/templates/sogo_eas.template.sh > /etc/nginx/conf.d/sogo_eas.active &&
|
||||||
|
. /etc/nginx/conf.d/templates/ssl_client_auth.template.sh &&
|
||||||
nginx -qt &&
|
nginx -qt &&
|
||||||
until ping phpfpm -c1 > /dev/null; do sleep 1; done &&
|
until ping phpfpm -c1 > /dev/null; do sleep 1; done &&
|
||||||
until ping sogo -c1 > /dev/null; do sleep 1; done &&
|
until ping sogo -c1 > /dev/null; do sleep 1; done &&
|
||||||
@@ -387,6 +388,9 @@ services:
|
|||||||
until ping rspamd -c1 > /dev/null; do sleep 1; done &&
|
until ping rspamd -c1 > /dev/null; do sleep 1; done &&
|
||||||
exec nginx -g 'daemon off;'"
|
exec nginx -g 'daemon off;'"
|
||||||
environment:
|
environment:
|
||||||
|
- DBNAME=${DBNAME}
|
||||||
|
- DBUSER=${DBUSER}
|
||||||
|
- DBPASS=${DBPASS}
|
||||||
- HTTPS_PORT=${HTTPS_PORT:-443}
|
- HTTPS_PORT=${HTTPS_PORT:-443}
|
||||||
- HTTP_PORT=${HTTP_PORT:-80}
|
- HTTP_PORT=${HTTP_PORT:-80}
|
||||||
- MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME}
|
- MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME}
|
||||||
@@ -406,6 +410,7 @@ services:
|
|||||||
- ./data/web/inc/functions.auth.inc.php:/mailcowauth/functions.auth.inc.php:z
|
- ./data/web/inc/functions.auth.inc.php:/mailcowauth/functions.auth.inc.php:z
|
||||||
- ./data/web/inc/sessions.inc.php:/mailcowauth/sessions.inc.php:z
|
- ./data/web/inc/sessions.inc.php:/mailcowauth/sessions.inc.php:z
|
||||||
- sogo-web-vol-1:/usr/lib/GNUstep/SOGo/
|
- sogo-web-vol-1:/usr/lib/GNUstep/SOGo/
|
||||||
|
- mysql-socket-vol-1:/var/run/mysqld/
|
||||||
ports:
|
ports:
|
||||||
- "${HTTPS_BIND:-}:${HTTPS_PORT:-443}:${HTTPS_PORT:-443}"
|
- "${HTTPS_BIND:-}:${HTTPS_PORT:-443}:${HTTPS_PORT:-443}"
|
||||||
- "${HTTP_BIND:-}:${HTTP_PORT:-80}:${HTTP_PORT:-80}"
|
- "${HTTP_BIND:-}:${HTTP_PORT:-80}:${HTTP_PORT:-80}"
|
||||||
|
|||||||
Reference in new issue
Block a user