aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Alexandre Alapetite <alexandre@alapetite.fr> 2019-01-02 21:43:05 +0100
committerGravatar GitHub <noreply@github.com> 2019-01-02 21:43:05 +0100
commit945cf832ad2c20c10704282d03326d8495d0ca4b (patch)
tree00b83a1b046d5cfe498e871743c572b826840203
parenta6623b7b2fa3f026a0ea30e49b1a221f7a4a8e55 (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.md36
-rw-r--r--app/Controllers/authController.php6
-rw-r--r--app/Models/Auth.php9
-rw-r--r--app/Models/UserDAO.php10
-rw-r--r--lib/Minz/Configuration.php17
-rw-r--r--lib/lib_rss.php13
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 '';
}