From 945cf832ad2c20c10704282d03326d8495d0ca4b Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Wed, 2 Jan 2019 21:43:05 +0100 Subject: HTTP authenfication fixes (#2204) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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 --- Docker/README.md | 36 ++++++++++++++++++++++++++++++++++++ app/Controllers/authController.php | 6 +++++- app/Models/Auth.php | 9 +++++---- app/Models/UserDAO.php | 10 +++++----- lib/Minz/Configuration.php | 17 +++++------------ 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 ''; } -- cgit v1.2.3 From 20223b8b01f47fba0858d854c5744fad5900b9cc Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 5 Jan 2019 12:33:18 +0100 Subject: Automatic API test (#2207) * Automatic API test Easier for end-user, smarter, and the guess testing of greader authorization token was not reliable. * Travis + minor --- p/api/greader.php | 21 ++++++++----------- p/api/index.php | 27 ++++++++++++++---------- p/scripts/api.js | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 23 deletions(-) create mode 100644 p/scripts/api.js diff --git a/p/api/greader.php b/p/api/greader.php index 7cd312f2c..d41430d3c 100644 --- a/p/api/greader.php +++ b/p/api/greader.php @@ -143,14 +143,11 @@ function checkCompatibility() { Minz_Log::warning('checkCompatibility() ' . debugInfo(), API_LOG); header('Content-Type: text/plain; charset=UTF-8'); if (PHP_INT_SIZE < 8 && !function_exists('gmp_init')) { - die('FAIL 64-bit or GMP extension!'); + die('FAIL 64-bit or GMP extension! Wrong PHP configuration.'); } - if ((!array_key_exists('HTTP_AUTHORIZATION', $_SERVER)) && //Apache mod_rewrite trick should be fine - (!array_key_exists('REDIRECT_HTTP_AUTHORIZATION', $_SERVER)) && //Apache mod_rewrite with FCGI - (empty($_SERVER['SERVER_SOFTWARE']) || (stripos($_SERVER['SERVER_SOFTWARE'], 'nginx') === false)) && //nginx should be fine - (empty($_SERVER['SERVER_SOFTWARE']) || (stripos($_SERVER['SERVER_SOFTWARE'], 'lighttpd') === false)) && //lighttpd should be fine - ((!function_exists('getallheaders')) || (stripos(php_sapi_name(), 'cgi') !== false))) { //Main problem is Apache/CGI mode - die('FAIL getallheaders! (probably)'); + $headerAuth = headerVariable('Authorization', 'GoogleLogin_auth'); + if ($headerAuth == '') { + die('FAIL get HTTP Authorization header! Wrong Web server configuration.'); } echo 'PASS'; exit(); @@ -913,6 +910,10 @@ FreshRSS_Context::$system_conf = Minz_Configuration::get('system'); if (!FreshRSS_Context::$system_conf->api_enabled) { serviceUnavailable(); +} elseif (count($pathInfos) < 3) { + badRequest(); +} elseif ($pathInfos[1] === 'check' && $pathInfos[2] === 'compatibility') { + checkCompatibility(); } ini_set('session.use_cookies', '0'); @@ -927,9 +928,7 @@ if ($user !== '') { Minz_Session::_param('currentUser', $user); -if (count($pathInfos) < 3) { - badRequest(); -} elseif ($pathInfos[1] === 'accounts') { +if ($pathInfos[1] === 'accounts') { if (($pathInfos[2] === 'ClientLogin') && isset($_REQUEST['Email']) && isset($_REQUEST['Passwd'])) { clientLogin($_REQUEST['Email'], $_REQUEST['Passwd']); } @@ -1088,8 +1087,6 @@ if (count($pathInfos) < 3) { userInfo(); break; } -} elseif ($pathInfos[1] === 'check' && $pathInfos[2] === 'compatibility') { - checkCompatibility(); } badRequest(); diff --git a/p/api/index.php b/p/api/index.php index ee37b794b..d441099d7 100644 --- a/p/api/index.php +++ b/p/api/index.php @@ -5,6 +5,18 @@ FreshRSS API endpoints + + @@ -14,17 +26,11 @@
Your API address:
+
Google Reader API configuration test:
+
?
-

Fever compatible API

@@ -32,10 +38,9 @@ configuration (without %2F support)
+
Fever API configuration test:
+
?
- diff --git a/p/scripts/api.js b/p/scripts/api.js new file mode 100644 index 000000000..841b16a6a --- /dev/null +++ b/p/scripts/api.js @@ -0,0 +1,62 @@ +"use strict"; +/* jshint esversion:6, strict:global */ + +function check(url, next) { + if (!url || !next) { + return; + } + const req = new XMLHttpRequest(); + req.open('GET', url, true); + req.setRequestHeader('Authorization', 'GoogleLogin auth=test/1'); + req.onerror = function (e) { + next('FAIL: HTTP ' + e); + }; + req.onload = function () { + if (this.status == 200) { + next(this.response); + } else { + next('FAIL: HTTP error ' + this.status + ' ' + this.statusText); + } + }; + req.send(); +} + +const jsonVars = JSON.parse(document.getElementById('jsonVars').innerHTML); + +check(jsonVars.greader + '/check/compatibility', function next(result1) { + const greaderOutput = document.getElementById('greaderOutput'); + if (result1 === 'PASS') { + greaderOutput.innerHTML = '✔️ ' + result1; + } else { + check(jsonVars.greader + '/check%2Fcompatibility', function next(result2) { + if (result2 === 'PASS') { + greaderOutput.innerHTML = '⚠️ WARN: no %2F support, so some clients will not work!'; + } else { + check('./greader.php/check/compatibility', function next(result3) { + if (result3 === 'PASS') { + greaderOutput.innerHTML = '⚠️ WARN: Probable invalid base URL in ./data/config.php'; + } else { + greaderOutput.innerHTML = '❌ ' + result1; + } + }); + } + }); + } + }); + +check(jsonVars.fever + '?api', function next(result1) { + const feverOutput = document.getElementById('feverOutput'); + try { + JSON.parse(result1); + feverOutput.innerHTML = '✔️ PASS'; + } catch (ex) { + check('./fever.php?api', function next(result2) { + try { + JSON.parse(result2); + feverOutput.innerHTML = '⚠️ WARN: Probable invalid base URL in ./data/config.php'; + } catch (ex) { + feverOutput.innerHTML = '❌ ' + result1; + } + }); + } + }); -- cgit v1.2.3 From 802c264574902ea44c2c76ae098a6f58911fe114 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 6 Jan 2019 00:46:48 +0100 Subject: Copy syslog to STDERR (#2208) * Use openlog before syslog In order to have a copy on stderr when syslog is not available. * Take advantage of syslog for actualization Pipe cron job STDERR and syslog to Docker log Cf. https://github.com/FreshRSS/FreshRSS/pull/2202/commits/00bd467655b7c060cdae388519b2413d12d8cb0f --- Docker/Dockerfile | 11 +++++------ app/Models/Entry.php | 1 + app/actualize_script.php | 8 ++++++-- cli/_cli.php | 2 +- lib/favicons.php | 1 + lib/lib_install.php | 1 + lib/lib_rss.php | 7 +++++++ p/i/index.php | 2 ++ 8 files changed, 24 insertions(+), 9 deletions(-) diff --git a/Docker/Dockerfile b/Docker/Dockerfile index 2a25e567d..db034ff61 100644 --- a/Docker/Dockerfile +++ b/Docker/Dockerfile @@ -6,11 +6,10 @@ RUN apk add --no-cache \ php7-ctype php7-dom php7-fileinfo php7-iconv php7-json php7-session php7-simplexml php7-xmlreader php7-zlib \ php7-pdo_sqlite php7-pdo_mysql php7-pdo_pgsql -ENV FRESHRSS_ROOT /var/www/FreshRSS -RUN mkdir -p ${FRESHRSS_ROOT} /run/apache2/ -WORKDIR ${FRESHRSS_ROOT} +RUN mkdir -p /var/www/FreshRSS /run/apache2/ +WORKDIR /var/www/FreshRSS -COPY . ${FRESHRSS_ROOT} +COPY . /var/www/FreshRSS COPY ./Docker/*.Apache.conf /etc/apache2/conf.d/ RUN rm -f /etc/apache2/conf.d/languages.conf /etc/apache2/conf.d/info.conf \ @@ -21,8 +20,8 @@ RUN rm -f /etc/apache2/conf.d/languages.conf /etc/apache2/conf.d/info.conf \ /etc/apache2/httpd.conf && \ sed -r -i "/^\s*(CustomLog|ErrorLog|Listen) /s/^/#/" \ /etc/apache2/httpd.conf && \ - echo "17,37 * * * * php ${FRESHRSS_ROOT}/app/actualize_script.php 2>&1 | tee /tmp/FreshRSS.log" >> \ - /var/spool/cron/crontabs/www-data + echo "17,37 * * * * su apache -s /bin/sh -c 'php /var/www/FreshRSS/app/actualize_script.php' 2>> /proc/1/fd/2 > /tmp/FreshRSS.log" >> \ + /var/spool/cron/crontabs/root ENV CRON_MIN '' ENTRYPOINT ["./Docker/entrypoint.sh"] diff --git a/app/Models/Entry.php b/app/Models/Entry.php index 985276734..f2f3d08fe 100644 --- a/app/Models/Entry.php +++ b/app/Models/Entry.php @@ -209,6 +209,7 @@ class FreshRSS_Entry extends Minz_Model { $feed_timeout = empty($attributes['timeout']) ? 0 : intval($attributes['timeout']); if ($system_conf->simplepie_syslog_enabled) { + prepareSyslog(); syslog(LOG_INFO, 'FreshRSS GET ' . SimplePie_Misc::url_remove_credentials($url)); } diff --git a/app/actualize_script.php b/app/actualize_script.php index ba9660a14..f1dec5640 100755 --- a/app/actualize_script.php +++ b/app/actualize_script.php @@ -12,6 +12,9 @@ if (defined('STDOUT')) { fwrite(STDOUT, 'Starting feed actualization at ' . $begin_date->format('c') . "\n"); //Unbuffered } +prepareSyslog(); +syslog(LOG_INFO, 'FreshRSS Start feeds actualization...'); + // Set the header params ($_GET) to call the FRSS application. $_GET['c'] = 'feed'; $_GET['a'] = 'actualize'; @@ -64,7 +67,7 @@ foreach ($users as $user) { if (!invalidateHttpCache()) { Minz_Log::warning('FreshRSS write access problem in ' . join_path(USERS_PATH, $user, 'log.txt'), ADMIN_LOG); if (defined('STDERR')) { - fwrite(STDERR, 'Write access problem in ' . join_path(USERS_PATH, $user, 'log.txt') . "\n"); + fwrite(STDERR, 'FreshRSS write access problem in ' . join_path(USERS_PATH, $user, 'log.txt') . "\n"); } } } @@ -75,7 +78,8 @@ if (defined('STDOUT')) { $end_date = date_create('now'); $duration = date_diff($end_date, $begin_date); fwrite(STDOUT, 'Ending feed actualization at ' . $end_date->format('c') . "\n"); //Unbuffered - fwrite(STDOUT, 'Feed actualizations took ' . $duration->format('%a day(s), %h hour(s), %i minute(s) and %s seconds') . ' for ' . count($users) . " users\n"); //Unbuffered + fwrite(STDOUT, 'Feed actualizations took ' . $duration->format('%a day(s), %h hour(s), %i minute(s) and %s seconds') . ' for ' . count($users) . " users\n"); //Unbuffered } echo 'End.', "\n"; ob_end_flush(); +syslog(LOG_INFO, 'FreshRSS feeds actualization done.'); diff --git a/cli/_cli.php b/cli/_cli.php index 72629171c..e8fb6ae42 100644 --- a/cli/_cli.php +++ b/cli/_cli.php @@ -4,7 +4,7 @@ if (php_sapi_name() !== 'cli') { } require(__DIR__ . '/../constants.php'); -require(LIB_PATH . '/lib_rss.php'); +require(LIB_PATH . '/lib_rss.php'); //Includes class autoloader require(LIB_PATH . '/lib_install.php'); Minz_Configuration::register('system', diff --git a/lib/favicons.php b/lib/favicons.php index fe2e65f1f..7a2d1187e 100644 --- a/lib/favicons.php +++ b/lib/favicons.php @@ -22,6 +22,7 @@ function isImgMime($content) { } function downloadHttp(&$url, $curlOptions = array()) { + prepareSyslog(); syslog(LOG_INFO, 'FreshRSS Favicon GET ' . $url); if (substr($url, 0, 2) === '//') { $url = 'https:' . $url; diff --git a/lib/lib_install.php b/lib/lib_install.php index d51a37e58..6e4df4e9c 100644 --- a/lib/lib_install.php +++ b/lib/lib_install.php @@ -81,6 +81,7 @@ function generateSalt() { function checkDb(&$dbOptions) { $dsn = ''; $driver_options = null; + prepareSyslog(); try { switch ($dbOptions['type']) { case 'mysql': diff --git a/lib/lib_rss.php b/lib/lib_rss.php index 168309563..738ccf641 100644 --- a/lib/lib_rss.php +++ b/lib/lib_rss.php @@ -202,12 +202,19 @@ function html_only_entity_decode($text) { return strtr($text, $htmlEntitiesOnly); } +function prepareSyslog() { + return openlog("FreshRSS", LOG_PERROR | LOG_PID, LOG_USER); +} + function customSimplePie($attributes = array()) { $system_conf = Minz_Configuration::get('system'); $limits = $system_conf->limits; $simplePie = new SimplePie(); $simplePie->set_useragent(FRESHRSS_USERAGENT); $simplePie->set_syslog($system_conf->simplepie_syslog_enabled); + if ($system_conf->simplepie_syslog_enabled) { + prepareSyslog(); + } $simplePie->set_cache_location(CACHE_PATH); $simplePie->set_cache_duration($limits['cache_duration']); diff --git a/p/i/index.php b/p/i/index.php index 5bc9c0d76..8a4f529d4 100755 --- a/p/i/index.php +++ b/p/i/index.php @@ -50,5 +50,7 @@ if (file_exists(DATA_PATH . '/do-install.txt')) { echo '### Fatal error! ###
', "\n"; Minz_Log::error($e->getMessage()); echo 'See logs files.'; + prepareSyslog(); + syslog(LOG_INFO, 'FreshRSS Fatal error! ' . $e->getMessage()); } } -- cgit v1.2.3 From 6c423881d09fe7f3b52987359e74579c6f2025e3 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 6 Jan 2019 01:07:58 +0100 Subject: Forgotten cron fix https://github.com/FreshRSS/FreshRSS/pull/2208 --- Docker/entrypoint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Docker/entrypoint.sh b/Docker/entrypoint.sh index ba5d12bf2..d4e1808bc 100755 --- a/Docker/entrypoint.sh +++ b/Docker/entrypoint.sh @@ -6,7 +6,7 @@ chown -R :www-data . chmod -R g+r . && chmod -R g+w ./data/ if [ -n "$CRON_MIN" ]; then - sed -r -i "\#FreshRSS#s#^[^ ]+ #$CRON_MIN #" /var/spool/cron/crontabs/www-data + sed -r -i "\#FreshRSS#s#^[^ ]+ #$CRON_MIN #" /var/spool/cron/crontabs/root fi exec "$@" -- cgit v1.2.3 From 15d74d934708896706278574af159a9dcb3a4313 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 6 Jan 2019 12:07:51 +0100 Subject: Changelog + Revert mistakes from 2202 and 2204 (#2210) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * GMP is needed because Alpine on e.g. ARM runs 32-bit https://github.com/FreshRSS/FreshRSS/pull/2202 * Remove documentation for Træfik HTTP authentication as it is not compatible with API https://github.com/FreshRSS/FreshRSS/pull/2204 https://github.com/FreshRSS/FreshRSS/pull/2208 https://github.com/FreshRSS/FreshRSS/pull/2207 --- CHANGELOG.md | 15 +++++++++++++++ Docker/Dockerfile | 2 +- Docker/README.md | 23 ++++------------------- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a77c7b703..ff306cb7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,21 @@ * Bug fixing * Fix missing HTTP `X-Forwarded-Prefix` in cookie path behind a reverse-proxy [#2201](https://github.com/FreshRSS/FreshRSS/pull/2201) +* Deployment + * Docker improvements [#2202](https://github.com/FreshRSS/FreshRSS/pull/2202) + * Performance: Hard-include Apache .htaccess to avoid having to scan for changes in those files + * Performance: Disable unused Apache security check of symlinks + * Performance: Disable unused Apache modules + * Add option to mount custom `.htaccess` for HTTP authentication + * Docker logs gets PHP syslog messages (e.g. from cron job and when fetching external content) + * Send a copy of PHP syslog messages to STDERR [#2208](https://github.com/FreshRSS/FreshRSS/pull/2208) + * Run Docker cron job with Apache user instead of root [#2208](https://github.com/FreshRSS/FreshRSS/pull/2208) + * Accept HTTP header `X-WebAuth-User` for delegated HTTP Authentication [#2204](https://github.com/FreshRSS/FreshRSS/pull/2204) +* API + * Automatic test of API configuration [#2207](https://github.com/FreshRSS/FreshRSS/pull/2207) + * Use Apache SetEnvIf module if available and fall-back to RewriteRule [#2202](https://github.com/FreshRSS/FreshRSS/pull/2202) +* Security + * Fixes when HTTP user does not exist in FreshRSS [#2204](https://github.com/FreshRSS/FreshRSS/pull/2204) ## 2018-12-22 FreshRSS 1.13.0 diff --git a/Docker/Dockerfile b/Docker/Dockerfile index db034ff61..52ac5f2fc 100644 --- a/Docker/Dockerfile +++ b/Docker/Dockerfile @@ -2,7 +2,7 @@ FROM alpine:3.8 RUN apk add --no-cache \ apache2 php7-apache2 \ - php7 php7-curl php7-intl php7-mbstring php7-xml php7-zip \ + php7 php7-curl php7-gmp php7-intl php7-mbstring php7-xml php7-zip \ php7-ctype php7-dom php7-fileinfo php7-iconv php7-json php7-session php7-simplexml php7-xmlreader php7-zlib \ php7-pdo_sqlite php7-pdo_mysql php7-pdo_pgsql diff --git a/Docker/README.md b/Docker/README.md index b991409bd..1a915f326 100644 --- a/Docker/README.md +++ b/Docker/README.md @@ -205,35 +205,20 @@ 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`: +In particular, if you want FreshRSS to use HTTP-based login (instead of the easier Web form login), 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 \ + -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`: +Example of `/your/.htaccess` referring to `/your/.htpasswd`: ``` AuthUserFile /var/www/FreshRSS/data/.htpasswd AuthName "FreshRSS" -- cgit v1.2.3