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 5521cf7e..bd3be6cb 100644 --- a/src/app/Http/Controllers/API/V4/NGINXController.php +++ b/src/app/Http/Controllers/API/V4/NGINXController.php @@ -1,249 +1,255 @@ normalizeUsername($request->headers->get('Php-Auth-User', "")); $password = $request->headers->get('Php-Auth-Pw', null); if (empty($username)) { //Allow unauthenticated requests return response(""); } 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 Whether 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; } }