diff --git a/plugins/proxy-auth/README.md b/plugins/proxy-auth/README.md index 0a4c5dc345..7a3004d702 100644 --- a/plugins/proxy-auth/README.md +++ b/plugins/proxy-auth/README.md @@ -12,7 +12,7 @@ The following example is for Traefik with Authelia and Dovecot as mailserver. The following steps are require in SnappyMail: -- To open SnappyMail through a reverse proxy server, make sure to enable the correct secfetch policies: ```mode=navigate,dest=document,site=cross-site,user=true``` in the admin panel -> Config -> Security -> secfetch_allow. +- To open SnappyMail through a reverse proxy server (with redirect of authentication system), make sure to enable the correct secfetch policies: ```mode=navigate,dest=document,site=cross-site,user=true;mode=navigate,dest=document,site=same-site,user=true``` in the admin panel -> Config -> Security -> secfetch_allow. - Activate plugin in admin panel -> Extensions - Configure the plugin with the required data: - Master User Separator is dependent on Dovecot config (see below) @@ -21,6 +21,7 @@ The following steps are require in SnappyMail: - Header Name is dependent on authentication solution. This is the header containing the name of currently logged in user. In case of Authelia, this is "Remote-User". - Check Proxy: Since this plugin partially bypasses authentication, it is important to only allow this access from well-defined hosts. It is highly recommended to activate this option! - When checking for reverse proxy, it is required to set the IP filter to either an IP address or a subnet. + - Automatic Login: Automatically logs in the user of user header is present (see below) This concludes the setup of SnappyMail. @@ -50,12 +51,16 @@ passdb { You then need to create a master user in /etc/dovecot/master-users: ``` -admin:PASSWORD +admin:PASSWORD::::::allow_nets=local,172.17.0.0/16 ``` where the encrypted password ```PASSWORD``` can be created from a cleartext password with ```doveadm pw -s CRYPT```. It should start with ```{CRYPT}```. Username and password need to configured in the SnappyMail ProxyAuth plugin (see above). +You likely also want to limit the access by an IP address filter, e.g., to ```local,172.17.0.0/16```, if you are running Postfix (```local```) and within a default Docker environment (```172.17.0.0/16```). +Otherwise, master user login (assuming password is known) is possible from every connectable system. +This is an unnecessary security risk. + Additionally, you need to set the master user separator in /etc/dovecot/conf.d/10-auth.conf, e.g., ```auth_master_user_separator = *```. The separator needs to be configured in the SnappyMail ProxyAuth plugin (see above). @@ -64,3 +69,14 @@ The separator needs to be configured in the SnappyMail ProxyAuth plugin (see abo Once configured correctly, you should be able to access SnappyMail through your reverse proxy at ```https://snappymail.tld/?ProxyAuth```. If your reverse proxy provides the username in the configured header (e.g., Remote-User), you will automatically be logged in to your account. If not, you will be redirected to the login page. + +## Automatic Login + +By default, automatic login is activated. +Behind the scenes, this checks for the existence of the configured user header (through ```/?UserHeaderSet```) and automatically redirects to ```https://snappymail.tld/?ProxyAuth```, trying to log in the user. +Note that due to this implementation, logout is impossible, as once logged out, the user will automatically be logged in again. +The user is always considered logged in, as authentication is handled through reverse proxy and authentication system. + +Auto login can be disabled in the plugin settings. +You can also change the logout link in admin panel -> Config -> custom_logout_link to the one of your authentication system, e.g., ```https://auth.yourdomain.com/logout```. +In this case, you can log out from your overall system via SnappyMail. diff --git a/plugins/proxy-auth/index.php b/plugins/proxy-auth/index.php index fb108d8ce2..a69dd23de5 100644 --- a/plugins/proxy-auth/index.php +++ b/plugins/proxy-auth/index.php @@ -15,7 +15,9 @@ class ProxyAuthPlugin extends \RainLoop\Plugins\AbstractPlugin public function Init() : void { + $this->addJs('js/auto-login.js'); $this->addPartHook('ProxyAuth', 'ServiceProxyAuth'); + $this->addPartHook('UserHeaderSet', 'ServiceUserHeaderSet'); $this->addHook('login.credentials', 'MapEmailAddress'); } @@ -140,6 +142,28 @@ public function ServiceProxyAuth() : bool return true; } + public function ServiceUserHeaderSet() : bool + { + $oActions = \RainLoop\Api::Actions(); + + $oLogger = $oActions->Logger(); + $sLevel = LOG_DEBUG; + $sPrefix = "ProxyAuth"; + + $sHeaderName = \trim($this->Config()->getDecrypted('plugin', 'header_name', '')); + + $sRemoteUser = $this->Manager()->Actions()->Http()->GetHeader($sHeaderName); + $sMsg = "Remote User: " . $sRemoteUser; + $oLogger->Write($sMsg, $sLevel, $sPrefix); + + if (strlen($sRemoteUser) > 0) { + \MailSo\Base\Http::StatusHeader('200'); + } else { + \MailSo\Base\Http::StatusHeader('401'); + } + return true; + } + protected function configMapping() : array { return array( @@ -178,7 +202,13 @@ protected function configMapping() : array ->SetType(\RainLoop\Enumerations\PluginPropertyType::STRING_TEXT) ->SetDescription('IP or Subnet of proxy, auth header will only be accepted from this address') ->SetDefaultValue('10.1.0.0/24') - ->SetEncrypted() + ->SetEncrypted(), + \RainLoop\Plugins\Property::NewInstance('auto_login') + ->SetAllowedInJs(true) + ->SetLabel('Activate automatic login') + ->SetType(\RainLoop\Enumerations\PluginPropertyType::BOOL) + ->SetDescription('Activates automatic login, if User Header is set (note: Use custom_logout_link to enable logout, see plugin README)') + ->SetDefaultValue(true) ); } } diff --git a/plugins/proxy-auth/js/auto-login.js b/plugins/proxy-auth/js/auto-login.js new file mode 100644 index 0000000000..6d2c72b1c4 --- /dev/null +++ b/plugins/proxy-auth/js/auto-login.js @@ -0,0 +1,32 @@ +(rl => { + + rl && addEventListener('rl-view-model', e => { + const id = e.detail.viewModelTemplateID; + if (e.detail && ('Login' === id)) { + let + auto_login = window.rl.pluginSettingsGet('proxy-auth', 'auto_login'); + ; + + const + ForwardProxyAuth = () => { + if (auto_login) { + var xhr = new XMLHttpRequest(); + xhr.open("GET", "/?UserHeaderSet", true); + + xhr.onreadystatechange = function () { + if (xhr.readyState == 4 && xhr.status == 200) { + window.location.href = "/?ProxyAuth"; + } + }; + + xhr.send(); + } + }; + + window.ForwardProxyAuth = ForwardProxyAuth; + + ForwardProxyAuth(); + } + }); +})(window.rl); +