diff options
| author | 2019-01-02 21:43:05 +0100 | |
|---|---|---|
| committer | 2019-01-02 21:43:05 +0100 | |
| commit | 945cf832ad2c20c10704282d03326d8495d0ca4b (patch) | |
| tree | 00b83a1b046d5cfe498e871743c572b826840203 | |
| parent | a6623b7b2fa3f026a0ea30e49b1a221f7a4a8e55 (diff) | |
HTTP authenfication fixes (#2204)
* Security fixes when HTTP user does not exist in FreshRSS
* Accept HTTP header X-WebAuth-User for delegated HTTP Authentication (e.g. Træfik)
* Document delegated HTTP authentication from https://github.com/FreshRSS/FreshRSS/pull/2202
| -rw-r--r-- | Docker/README.md | 36 | ||||
| -rw-r--r-- | app/Controllers/authController.php | 6 | ||||
| -rw-r--r-- | app/Models/Auth.php | 9 | ||||
| -rw-r--r-- | app/Models/UserDAO.php | 10 | ||||
| -rw-r--r-- | lib/Minz/Configuration.php | 17 | ||||
| -rw-r--r-- | lib/lib_rss.php | 13 |
6 files changed, 62 insertions, 29 deletions
diff --git a/Docker/README.md b/Docker/README.md index 6b3871c6b..b991409bd 100644 --- a/Docker/README.md +++ b/Docker/README.md @@ -205,6 +205,42 @@ sudo docker run -d --restart unless-stopped --log-opt max-size=10m \ ## More deployment options +### Use HTTP-based login (advanced users) + +FreshRSS allows logins using either a Web form (easiest) or based on HTTP authentication. +If you want HTTP authentication, [Træfik can do it](https://docs.traefik.io/configuration/entrypoints/#authentication) (otherwise, see section below for giving this task to Apache inside the FreshRSS Docker image): + +``` +sudo docker run ... + --label traefik.frontend.auth.basic.users='admin:$2y$05$BJ3eexf8gkyfHR1L38nVMeQ2RbQ5PF6KW4/PlttXeR6IOGZKH4sbC,alice:$2y$05$0vv8eexRq4qujzyBCYh6a.bo/KUvuXCmjJ54RqEHBApaHdQrpzFJC' \ + --label traefik.frontend.auth.removeheader=true \ + --label traefik.frontend.auth.headerField=X-WebAuth-User \ + --name freshrss freshrss/freshrss +``` + +N.B.: You can create password hashes for instance with: `htpasswd -nB alice` + +### Custom Apache configuration (advanced users) + +Changes in Apache `.htaccess` files are applied when restarting the container. +In particular, if you want FreshRSS to use HTTP-based login (instead of the easier Web form login, and instead of letting Træfik do it), you can mount your own `./FreshRSS/p/i/.htaccess`: + +``` +sudo docker run ... + -v ./your/.htaccess:/var/www/FreshRSS/p/i/.htaccess \ + -v ./your/.htpasswd:/var/www/FreshRSS/data/.htpasswd \ + ... + --name freshrss freshrss/freshrss +``` + +Example of `./your/.htaccess` referring to `./your/.htpasswd`: +``` +AuthUserFile /var/www/FreshRSS/data/.htpasswd +AuthName "FreshRSS" +AuthType Basic +Require valid-user +``` + ### Example with [docker-compose](https://docs.docker.com/compose/) A [docker-compose.yml](docker-compose.yml) file is given as an example, using PostgreSQL. In order to use it, you have to adapt: diff --git a/app/Controllers/authController.php b/app/Controllers/authController.php index 5ad1a51d9..3b2d78b19 100644 --- a/app/Controllers/authController.php +++ b/app/Controllers/authController.php @@ -79,8 +79,12 @@ class FreshRSS_auth_Controller extends Minz_ActionController { Minz_Request::forward(array('c' => 'auth', 'a' => 'formLogin')); break; case 'http_auth': + Minz_Error::error(403, array('error' => array(_t('feedback.access.denied'), + ' [HTTP Remote-User=' . htmlspecialchars(httpAuthUser(), ENT_NOQUOTES, 'UTF-8') . ']' + )), false); + break; case 'none': - // It should not happened! + // It should not happen! Minz_Error::error(404); default: // TODO load plugin instead diff --git a/app/Models/Auth.php b/app/Models/Auth.php index 9c3e31952..513a9cb2f 100644 --- a/app/Models/Auth.php +++ b/app/Models/Auth.php @@ -28,13 +28,13 @@ class FreshRSS_Auth { if (self::$login_ok) { self::giveAccess(); - } elseif (self::accessControl()) { - self::giveAccess(); + } elseif (self::accessControl() && self::giveAccess()) { FreshRSS_UserDAO::touch(); } else { // Be sure all accesses are removed! self::removeAccess(); } + return self::$login_ok; } /** @@ -60,7 +60,7 @@ class FreshRSS_Auth { return $current_user != ''; case 'http_auth': $current_user = httpAuthUser(); - $login_ok = $current_user != ''; + $login_ok = $current_user != '' && FreshRSS_UserDAO::exists($current_user); if ($login_ok) { Minz_Session::_param('currentUser', $current_user); } @@ -81,7 +81,7 @@ class FreshRSS_Auth { $user_conf = get_user_configuration($current_user); if ($user_conf == null) { self::$login_ok = false; - return; + return false; } $system_conf = Minz_Configuration::get('system'); @@ -102,6 +102,7 @@ class FreshRSS_Auth { Minz_Session::_param('loginOk', self::$login_ok); Minz_Session::_param('REMOTE_USER', httpAuthUser()); + return self::$login_ok; } /** diff --git a/app/Models/UserDAO.php b/app/Models/UserDAO.php index 5fb46c947..e9d3a7329 100644 --- a/app/Models/UserDAO.php +++ b/app/Models/UserDAO.php @@ -65,7 +65,7 @@ class FreshRSS_UserDAO extends Minz_ModelPdo { require_once(APP_PATH . '/SQL/install.sql.' . $db['type'] . '.php'); if ($db['type'] === 'sqlite') { - return unlink(join_path(DATA_PATH, 'users', $username, 'db.sqlite')); + return unlink(USERS_PATH . '/' . $username . '/db.sqlite'); } else { $userPDO = new Minz_ModelPdo($username); @@ -81,18 +81,18 @@ class FreshRSS_UserDAO extends Minz_ModelPdo { } } - public static function exist($username) { - return is_dir(join_path(DATA_PATH, 'users', $username)); + public static function exists($username) { + return is_dir(USERS_PATH . '/' . $username); } public static function touch($username = '') { if (!FreshRSS_user_Controller::checkUsername($username)) { $username = Minz_Session::param('currentUser', '_'); } - return touch(join_path(DATA_PATH, 'users', $username, 'config.php')); + return touch(USERS_PATH . '/' . $username . '/config.php'); } public static function mtime($username) { - return @filemtime(join_path(DATA_PATH, 'users', $username, 'config.php')); + return @filemtime(USERS_PATH . '/' . $username . '/config.php'); } } diff --git a/lib/Minz/Configuration.php b/lib/Minz/Configuration.php index 3e486d68e..aae3accc6 100644 --- a/lib/Minz/Configuration.php +++ b/lib/Minz/Configuration.php @@ -27,23 +27,16 @@ class Minz_Configuration { /** * Parse a file and return its data. * - * If the file does not contain a valid PHP code returning an array, an - * empty array is returned anyway. - * * @param $filename the name of the file to parse. * @return an array of values - * @throws Minz_FileNotExistException if the file does not exist. + * @throws Minz_FileNotExistException if the file does not exist or is invalid. */ public static function load($filename) { - if (!file_exists($filename)) { - throw new Minz_FileNotExistException($filename); - } - - $data = include($filename); + $data = @include($filename); if (is_array($data)) { return $data; } else { - return array(); + throw new Minz_FileNotExistException($filename); } } @@ -117,7 +110,7 @@ class Minz_Configuration { $this->default_filename = $default_filename; $this->_configurationSetter($configuration_setter); - if (!is_null($this->default_filename)) { + if ($this->default_filename != null) { $this->data = self::load($this->default_filename); } @@ -126,7 +119,7 @@ class Minz_Configuration { $this->data, self::load($this->config_filename) ); } catch (Minz_FileNotExistException $e) { - if (is_null($this->default_filename)) { + if ($this->default_filename == null) { throw $e; } } diff --git a/lib/lib_rss.php b/lib/lib_rss.php index 333920c8c..168309563 100644 --- a/lib/lib_rss.php +++ b/lib/lib_rss.php @@ -364,9 +364,9 @@ function get_user_configuration($username) { join_path(FRESHRSS_PATH, 'config-user.default.php')); } catch (Minz_ConfigurationNamespaceException $e) { // namespace already exists, do nothing. - Minz_Log::warning($e->getMessage()); + Minz_Log::warning($e->getMessage(), USERS_PATH . '/_/log.txt'); } catch (Minz_FileNotExistException $e) { - Minz_Log::warning($e->getMessage()); + Minz_Log::warning($e->getMessage(), USERS_PATH . '/_/log.txt'); return null; } @@ -375,14 +375,13 @@ function get_user_configuration($username) { function httpAuthUser() { - if (isset($_SERVER['REMOTE_USER'])) { + if (!empty($_SERVER['REMOTE_USER'])) { return $_SERVER['REMOTE_USER']; - } - - if (isset($_SERVER['REDIRECT_REMOTE_USER'])) { + } elseif (!empty($_SERVER['REDIRECT_REMOTE_USER'])) { return $_SERVER['REDIRECT_REMOTE_USER']; + } elseif (!empty($_SERVER['HTTP_X_WEBAUTH_USER'])) { + return $_SERVER['HTTP_X_WEBAUTH_USER']; } - return ''; } |
