From 9c1bd5c18b3712e9e9aa6c328ab020a84c8eea73 Mon Sep 17 00:00:00 2001 From: andryyy Date: Thu, 11 Feb 2021 15:24:49 +0100 Subject: [PATCH] [Ejabberd] Add new Dockerfile --- data/Dockerfiles/ejabberd/Dockerfile | 33 +++ data/Dockerfiles/ejabberd/authenticator | 19 ++ .../Dockerfiles/ejabberd/docker-entrypoint.sh | 93 +++++++ .../ejabberd/mailcowCommandExecutor.php | 229 ++++++++++++++++++ 4 files changed, 374 insertions(+) create mode 100644 data/Dockerfiles/ejabberd/Dockerfile create mode 100755 data/Dockerfiles/ejabberd/authenticator create mode 100755 data/Dockerfiles/ejabberd/docker-entrypoint.sh create mode 100644 data/Dockerfiles/ejabberd/mailcowCommandExecutor.php diff --git a/data/Dockerfiles/ejabberd/Dockerfile b/data/Dockerfiles/ejabberd/Dockerfile new file mode 100644 index 00000000..7758df27 --- /dev/null +++ b/data/Dockerfiles/ejabberd/Dockerfile @@ -0,0 +1,33 @@ +FROM ejabberd/ecs:21.01 + +LABEL maintainer "Andre Peters " + +ENV GOSU_VERSION 1.11 + +# We need to copy cert files, dropping rights at a later point +USER root + +RUN apk add --update --no-cache su-exec \ + bash \ + tini \ + jq \ + mariadb-client \ + redis \ + tzdata \ + curl \ + openssl \ + bind-tools \ + composer \ + php7-pdo \ + php7-pdo_mysql \ + php7-ctype + +RUN mkdir -p /var/www/authentication && \ + cd /var/www/authentication && \ + composer require leesherwood/ejabberd-php-auth monolog/monolog + +COPY docker-entrypoint.sh /docker-entrypoint.sh +COPY authenticator /var/www/authentication/authenticator +COPY mailcowCommandExecutor.php /var/www/authentication/vendor/leesherwood/ejabberd-php-auth/src/CommandExecutors/mailcowCommandExecutor.php + +ENTRYPOINT ["/docker-entrypoint.sh"] diff --git a/data/Dockerfiles/ejabberd/authenticator b/data/Dockerfiles/ejabberd/authenticator new file mode 100755 index 00000000..6f3acd0a --- /dev/null +++ b/data/Dockerfiles/ejabberd/authenticator @@ -0,0 +1,19 @@ +#!/usr/bin/env php +pushHandler($stdoutHandler); + +$executor = new mailcowCommandExecutor(); + +$application = new AuthenticationService($logger, $executor); + +$application->run(); diff --git a/data/Dockerfiles/ejabberd/docker-entrypoint.sh b/data/Dockerfiles/ejabberd/docker-entrypoint.sh new file mode 100755 index 00000000..355bb32f --- /dev/null +++ b/data/Dockerfiles/ejabberd/docker-entrypoint.sh @@ -0,0 +1,93 @@ +#!/bin/bash +set -e + +until dig +short mailcow.email @unbound > /dev/null; do + echo "Waiting for DNS..." + sleep 1 +done + +until nc phpfpm 9001 -z; do + echo "Waiting for PHP on port 9001..." + sleep 3 +done + +until nc phpfpm 9002 -z; do + echo "Waiting for PHP on port 9002..." + sleep 3 +done + +# Wait for MySQL to warm-up +while ! mysqladmin status --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do + echo "Waiting for database to come up..." + sleep 2 +done + +if [[ -z "$(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -B -e 'SELECT domain FROM domain WHERE xmpp = 1')" ]]; then + echo "No XMPP host configured, sleeping the sleep of the righteous, waiting for someone to wake me up..." + exec su-exec ejabberd tini -g -- sleep 365d +fi + +# We dont want to give global write access to ejabberd in this directory +chown -R root:root /var/www/authentication + +[ ! -f /sqlite/sqlite.db ] && cp /sqlite/sqlite_template.db /sqlite/sqlite.db + +# Write access to upload directory and log file for authenticator +touch /var/www/authentication/auth.log +chown -R ejabberd:ejabberd /var/www/upload \ + /var/www/authentication/auth.log \ + /sqlite + +# ACL file for vhosts, hosts file for vhosts +touch /ejabberd/ejabberd_acl.yml \ + /ejabberd/ejabberd_hosts.yml \ + /ejabberd/ejabberd_macros.yml +chmod 644 /ejabberd/ejabberd_acl.yml \ + /ejabberd/ejabberd_hosts.yml \ + /ejabberd/ejabberd_macros.yml +chown 82:82 /ejabberd/ejabberd_acl.yml \ + /ejabberd/ejabberd_hosts.yml + +cat < /ejabberd/ejabberd_api.yml +# Autogenerated by mailcow +api_permissions: + "Reload by mailcow": + who: + - ip: "${IPV4_NETWORK}.0/24" + what: + - "reload_config" + - "restart" + - "list_certificates" + - "list_cluster" + - "join_cluster" + - "leave_cluster" + - "backup" + - "status" + - "stats" + - "muc_online_rooms" +EOF + +cat < /ejabberd/ejabberd_macros.yml +# Autogenerated by mailcow +define_macro: + 'MAILCOW_HOSTNAME': "${MAILCOW_HOSTNAME}" +EOF + +# Set open_basedir +sed -i 's/;open_basedir =/open_basedir = \/var\/www\/authentication/g' /etc/php7/php.ini + +sed -i "s/__DBUSER__/${DBUSER}/g" /var/www/authentication/vendor/leesherwood/ejabberd-php-auth/src/CommandExecutors/mailcowCommandExecutor.php +sed -i "s/__DBPASS__/${DBPASS}/g" /var/www/authentication/vendor/leesherwood/ejabberd-php-auth/src/CommandExecutors/mailcowCommandExecutor.php +sed -i "s/__DBNAME__/${DBNAME}/g" /var/www/authentication/vendor/leesherwood/ejabberd-php-auth/src/CommandExecutors/mailcowCommandExecutor.php + +# Run hooks +for file in /hooks/*; do + if [ -x "${file}" ]; then + echo "Running hook ${file}" + "${file}" + fi +done + +alias ejabberdctl="su-exec ejabberd /home/ejabberd/bin/ejabberdctl --node ejabberd@${MAILCOW_HOSTNAME}" + +exec su-exec ejabberd tini -g -- /home/ejabberd/bin/ejabberdctl --node ejabberd@${MAILCOW_HOSTNAME} foreground diff --git a/data/Dockerfiles/ejabberd/mailcowCommandExecutor.php b/data/Dockerfiles/ejabberd/mailcowCommandExecutor.php new file mode 100644 index 00000000..2ff33610 --- /dev/null +++ b/data/Dockerfiles/ejabberd/mailcowCommandExecutor.php @@ -0,0 +1,229 @@ + 1) { + $trunc_len = $components[0]; + $trunc_password = $components[1]; + + return substr($password, 0, $trunc_len) == $trunc_password; + } else { + return $password == $hash; + } + + case "SHA": + case "SHA1": + case "SHA256": + case "SHA512": + // SHA is an alias for SHA1 + $scheme = $scheme == "SHA" ? "sha1" : strtolower($scheme); + $hash = base64_decode($hash); + return hash_equals(hash($scheme, $password, true), $hash); + + case "SMD5": + return verify_salted_hash($hash, $password, 'md5', 16); + + case "SSHA": + return verify_salted_hash($hash, $password, 'sha1', 20); + + case "SSHA256": + return verify_salted_hash($hash, $password, 'sha256', 32); + + case "SSHA512": + return verify_salted_hash($hash, $password, 'sha512', 64); + + default: + return false; + } + } + return false; + } + + public function authenticate($username, $servername, $password) + { + $database_type = 'mysql'; + $database_sock = '/var/run/mysqld/mysqld.sock'; + $database_user = '__DBUSER__'; + $database_pass = '__DBPASS__'; + $database_name = '__DBNAME__'; + + $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, + PDO::ATTR_EMULATE_PREPARES => false + ]; + try { + $pdo = new PDO($dsn, $database_user, $database_pass, $opt); + } + catch (PDOException $e) { + return false; + } + if (!filter_var($username, FILTER_VALIDATE_EMAIL) && !ctype_alnum(str_replace(array('_', '.', '-'), '', $username))) { + return false; + } + $username = strtolower(trim($username)); + $stmt = $pdo->prepare("SELECT `password` FROM `mailbox` + INNER JOIN domain on mailbox.domain = domain.domain + WHERE `kind` NOT REGEXP 'location|thing|group' + AND `mailbox`.`active`= '1' + AND `domain`.`active`= '1' + AND `domain`.`xmpp` = '1' + AND JSON_UNQUOTE(JSON_VALUE(`mailbox`.`attributes`, '$.xmpp_access')) = '1' + AND CONCAT(`domain`.`xmpp_prefix`, '.', `domain`.`domain`) = :servername + AND `username` = CONCAT(:local_part, '@', `domain`.`domain`)"); + $stmt->execute(array(':local_part' => $username, ':servername' => $servername)); + $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); + foreach ($rows as $row) { + if (self::verify_hash($row['password'], $password) !== false) { + return true; + } + } + return false; + } + + /** + * Check if a user exists + * + * @param string $username + * @param string $servername + * + * @return bool + */ + public function userExists($username, $servername) + { + return true; + } + + /** + * Set a password for a user + * + * @param string $username + * @param string $servername + * @param string $password + * + * @return bool + */ + public function setPassword($username, $servername, $password) + { + return false; + } + + /** + * Register a user + * + * @param string $username + * @param string $servername + * @param string $password + * + * @return bool + */ + public function register($username, $servername, $password) + { + return false; + } + + /** + * Delete a user + * + * @param string $username + * @param string $servername + * + * @return bool + */ + public function removeUser($username, $servername) + { + return false; + } + + /** + * Delete a user with password validation + * + * @param string $username + * @param string $servername + * @param string $password + * + * @return bool + */ + public function removeUserWithPassword($username, $servername, $password) + { + return false; + } +} \ No newline at end of file