Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F9785633
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
13 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/docker/kolab/utils/24-roundcubeconfig.sh b/docker/kolab/utils/24-roundcubeconfig.sh
index 70401a25..13d78f47 100755
--- a/docker/kolab/utils/24-roundcubeconfig.sh
+++ b/docker/kolab/utils/24-roundcubeconfig.sh
@@ -1,13 +1,34 @@
#!/bin/bash
sed -i -r -e "s|$config\['kolab_files_url'\] = .*$|$config['kolab_files_url'] = 'https://' \. \$_SERVER['HTTP_HOST'] . '/chwala/';|g" /etc/roundcubemail/kolab_files.inc.php
sed -i -r -e "s|$config\['kolab_invitation_calendars'\] = .*$|$config['kolab_invitation_calendars'] = true;|g" /etc/roundcubemail/calendar.inc.php
sed -i -r -e "/^.*'contextmenu',$/a 'enigma'," /etc/roundcubemail/config.inc.php
sed -i -r -e "s|$config\['enigma_passwordless'\] = .*$|$config['enigma_passwordless'] = true;|g" /etc/roundcubemail/enigma.inc.php
sed -i -r -e "s|$config\['enigma_multihost'\] = .*$|$config['enigma_multihost'] = true;|g" /etc/roundcubemail/enigma.inc.php
echo "\$config['enigma_woat'] = true;" >> /etc/roundcubemail/enigma.inc.php
+# Run it over nginx for 2fa. We need to use startls because otherwise the proxy protocol doesn't work.
+sed -i -r -e "s|$config\['default_host'\] = .*$|$config['default_host'] = 'tls://127.0.0.1';|g" /etc/roundcubemail/config.inc.php
+sed -i -r -e "s|$config\['default_port'\] = .*$|$config['default_port'] = 144;|g" /etc/roundcubemail/config.inc.php
+
+# So we can just append
+sed -i "s/?>//g" /etc/roundcubemail/config.inc.php
+
+# Enable the PROXY protocol
+cat << EOF >> /etc/roundcubemail/config.inc.php
+ \$config['imap_conn_options'] = Array(
+ 'ssl' => Array(
+ 'verify_peer_name' => false,
+ 'verify_peer' => false,
+ 'allow_self_signed' => true
+ ),
+ 'proxy_protocol' => 2
+ );
+ \$config['proxy_whitelist'] = array('127.0.0.1');
+EOF
+
+echo "?>" >> /etc/roundcubemail/config.inc.php
diff --git a/docker/nginx/nginx.conf b/docker/nginx/nginx.conf
index 00e492f2..16114e3b 100644
--- a/docker/nginx/nginx.conf
+++ b/docker/nginx/nginx.conf
@@ -1,73 +1,87 @@
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log debug;
pid /run/nginx.pid;
# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;
events {
worker_connections 1024;
}
mail {
server_name imap.hosted.com;
auth_http 127.0.0.1:8000/api/webhooks/nginx;
auth_http_header Host services.APP_WEBSITE_DOMAIN;
proxy_pass_error_message on;
server {
listen 143;
protocol imap;
proxy on;
starttls on;
ssl_certificate SSL_CERTIFICATE_CERT;
ssl_certificate_key SSL_CERTIFICATE_KEY;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
}
+ server {
+ listen 144 proxy_protocol;
+ protocol imap;
+
+ proxy on;
+ starttls on;
+
+ ssl_certificate SSL_CERTIFICATE_CERT;
+ ssl_certificate_key SSL_CERTIFICATE_KEY;
+
+ ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
+ ssl_ciphers HIGH:!aNULL:!MD5;
+ }
+
server {
listen 465 ssl;
protocol smtp;
proxy on;
ssl_certificate SSL_CERTIFICATE_CERT;
ssl_certificate_key SSL_CERTIFICATE_KEY;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
}
server {
listen 587;
protocol smtp;
proxy on;
starttls on;
ssl_certificate SSL_CERTIFICATE_CERT;
ssl_certificate_key SSL_CERTIFICATE_KEY;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
}
server {
listen 993 ssl;
protocol imap;
proxy on;
ssl_certificate SSL_CERTIFICATE_CERT;
ssl_certificate_key SSL_CERTIFICATE_KEY;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
}
}
diff --git a/src/app/Http/Controllers/API/V4/NGINXController.php b/src/app/Http/Controllers/API/V4/NGINXController.php
index 1a74442e..f08224b0 100644
--- a/src/app/Http/Controllers/API/V4/NGINXController.php
+++ b/src/app/Http/Controllers/API/V4/NGINXController.php
@@ -1,273 +1,279 @@
<?php
namespace App\Http\Controllers\API\V4;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
class NGINXController extends Controller
{
/**
* Authorize with the provided credentials.
*
* @param string $login The login name
* @param string $password The password
* @param string $clientIP The client ip
*
* @return \App\User The user
*
* @throws \Exception If the authorization fails.
*/
private function authorizeRequest($login, $password, $clientIP)
{
if (empty($login)) {
throw new \Exception("Empty login");
}
if (empty($password)) {
throw new \Exception("Empty password");
}
if (empty($clientIP)) {
throw new \Exception("No client ip");
}
$user = \App\User::where('email', $login)->first();
if (!$user) {
throw new \Exception("User not found");
}
// TODO: validate the user's domain is A-OK (active, confirmed, not suspended, ldapready)
// TODO: validate the user is A-OK (active, not suspended, ldapready, imapready)
// TODO: we could use User::findAndAuthenticate() with some modifications here
if (!Hash::check($password, $user->password)) {
$attempt = \App\AuthAttempt::recordAuthAttempt($user, $clientIP);
// Avoid setting a password failure reason if we previously accepted the location.
if (!$attempt->isAccepted()) {
$attempt->reason = \App\AuthAttempt::REASON_PASSWORD;
$attempt->save();
$attempt->notify();
}
throw new \Exception("Password mismatch");
}
// validate country of origin against restrictions, otherwise bye bye
if (!$user->validateLocation($clientIP)) {
\Log::info("Failed authentication attempt due to country code mismatch for user: {$login}");
$attempt = \App\AuthAttempt::recordAuthAttempt($user, $clientIP);
$attempt->deny(\App\AuthAttempt::REASON_GEOLOCATION);
$attempt->notify();
throw new \Exception("Country code mismatch");
}
// TODO: Apply some sort of limit for Auth-Login-Attempt -- docs say it is the number of
// attempts over the same authAttempt.
// Check 2fa
if (\App\CompanionApp::where('user_id', $user->id)->exists()) {
$authAttempt = \App\AuthAttempt::recordAuthAttempt($user, $clientIP);
if (!$authAttempt->waitFor2FA()) {
throw new \Exception("2fa failed");
}
}
return $user;
}
/**
* Convert domain.tld\username into username@domain for activesync
*
* @param string $username The original username.
*
* @return string The username in canonical form
*/
private function normalizeUsername($username)
{
$usernameParts = explode("\\", $username);
if (count($usernameParts) == 2) {
$username = $usernameParts[1];
if (!strpos($username, '@') && !empty($usernameParts[0])) {
$username .= '@' . $usernameParts[0];
}
}
return $username;
}
/**
* Authentication request from the ngx_http_auth_request_module
*
* @param \Illuminate\Http\Request $request The API request.
*
* @return \Illuminate\Http\Response The response
*/
public function httpauth(Request $request)
{
/**
Php-Auth-Pw: simple123
Php-Auth-User: john@kolab.org
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: cross-site
Sec-Gpc: 1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:93.0) Gecko/20100101 Firefox/93.0
X-Forwarded-For: 31.10.153.58
X-Forwarded-Proto: https
X-Original-Uri: /iRony/
X-Real-Ip: 31.10.153.58
*/
$username = $this->normalizeUsername($request->headers->get('Php-Auth-User', ""));
$password = $request->headers->get('Php-Auth-Pw', null);
if (empty($password)) {
\Log::debug("Authentication attempt failed: Empty password provided.");
return response("", 401);
}
try {
$this->authorizeRequest(
$username,
$password,
$request->headers->get('X-Real-Ip', null),
);
} catch (\Exception $e) {
\Log::debug("Authentication attempt failed: {$e->getMessage()}");
return response("", 403);
}
\Log::debug("Authentication attempt succeeded");
return response("");
}
/**
* Authentication request.
*
* @todo: Separate IMAP(+STARTTLS) from IMAPS, same for SMTP/submission. =>
* I suppose that's not necessary given that we have the information avialable in the headers?
*
* @param \Illuminate\Http\Request $request The API request.
*
* @return \Illuminate\Http\Response The response
*/
public function authenticate(Request $request)
{
/**
* Auth-Login-Attempt: 1
* Auth-Method: plain
* Auth-Pass: simple123
* Auth-Protocol: imap
* Auth-Ssl: on
* Auth-User: john@kolab.org
* Client-Ip: 127.0.0.1
* Host: 127.0.0.1
*
* Auth-SSL: on
* Auth-SSL-Verify: SUCCESS
* Auth-SSL-Subject: /CN=example.com
* Auth-SSL-Issuer: /CN=example.com
* Auth-SSL-Serial: C07AD56B846B5BFF
* Auth-SSL-Fingerprint: 29d6a80a123d13355ed16b4b04605e29cb55a5ad
*/
$password = $request->headers->get('Auth-Pass', null);
+ $username = $request->headers->get('Auth-User', null);
+ $ip = $request->headers->get('Client-Ip', null);
+ $proxy_ip = $request->headers->get('Proxy-Protocol-Addr', null);
+ if ($proxy_ip) {
+ $ip = $proxy_ip;
+ }
try {
$user = $this->authorizeRequest(
- $request->headers->get('Auth-User', null),
+ $username,
$password,
- $request->headers->get('Client-Ip', null),
+ $ip,
);
} catch (\Exception $e) {
return $this->byebye($request, $e->getMessage());
}
// All checks passed
switch ($request->headers->get('Auth-Protocol')) {
case "imap":
return $this->authenticateIMAP($request, $user->getSetting('guam_enabled', false), $password);
case "smtp":
return $this->authenticateSMTP($request, $password);
default:
return $this->byebye($request, "unknown protocol in request");
}
}
/**
* Create an imap authentication response.
*
* @param \Illuminate\Http\Request $request The API request.
* @param bool $prefGuam Wether or not guam is enabled.
* @param string $password The password to include in the response.
*
* @return \Illuminate\Http\Response The response
*/
private function authenticateIMAP(Request $request, $prefGuam, $password)
{
if ($prefGuam) {
$port = \config('imap.guam_port');
} else {
$port = \config('imap.imap_port');
}
$response = response("")->withHeaders(
[
"Auth-Status" => "OK",
"Auth-Server" => \config('imap.host'),
"Auth-Port" => $port,
"Auth-Pass" => $password
]
);
return $response;
}
/**
* Create an smtp authentication response.
*
* @param \Illuminate\Http\Request $request The API request.
* @param string $password The password to include in the response.
*
* @return \Illuminate\Http\Response The response
*/
private function authenticateSMTP(Request $request, $password)
{
$response = response("")->withHeaders(
[
"Auth-Status" => "OK",
"Auth-Server" => \config('smtp.host'),
"Auth-Port" => \config('smtp.port'),
"Auth-Pass" => $password
]
);
return $response;
}
/**
* Create a failed-authentication response.
*
* @param \Illuminate\Http\Request $request The API request.
* @param string $reason The reason for the failure.
*
* @return \Illuminate\Http\Response The response
*/
private function byebye(Request $request, $reason = null)
{
\Log::debug("Byebye: {$reason}");
$response = response("")->withHeaders(
[
"Auth-Status" => "authentication failure",
"Auth-Wait" => 3
]
);
return $response;
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Mon, Aug 17, 7:20 PM (1 d, 14 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1261572
Default Alt Text
(13 KB)
Attached To
Mode
R2 kolab
Attached
Detach File
Event Timeline
Log In to Comment