Просмотр исходного кода

Add HTTP_REMOTE_USER header for auth (#4063)

* add HTTP_REMOTE_USER header for auth

* add ip whitelist for HTTP_REMOTE_USER header

* add IPv6 support for header auth

* fix formatting

* A few fixes

* Add some default trusted sources

* Fix IPv6 doc

* More standard header names

Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
drosoCode 4 лет назад
Родитель
Сommit
2aba861bc9
4 измененных файлов с 74 добавлено и 2 удалено
  1. 1 0
      app/Models/SystemConfiguration.php
  2. 8 0
      config.default.php
  3. 11 1
      docs/en/admins/09_AccessControl.md
  4. 54 1
      lib/lib_rss.php

+ 1 - 0
app/Models/SystemConfiguration.php

@@ -22,6 +22,7 @@
  * @property-read string $salt
  * @property-read bool $simplepie_syslog_enabled
  * @property string $unsafe_autologin_enabled
+ * @property-read array<string> $trusted_sources
  */
 class FreshRSS_SystemConfiguration extends Minz_Configuration {
 

+ 8 - 0
config.default.php

@@ -189,4 +189,12 @@ return array(
 
 	# Disable self-update,
 	'disable_update' => false,
+
+	# Trusted IPs that are allowed to send unsafe headers
+	# Please read the documentation, before configuring this
+	# https://freshrss.github.io/FreshRSS/en/admins/09_AccessControl.html
+	'trusted_sources' => [
+		'127.0.0.0/8',
+		'::1/128',
+	]
 );

+ 11 - 1
docs/en/admins/09_AccessControl.md

@@ -15,13 +15,23 @@ You may also choose to use HTTP Authentication provided by your web server.[^1]
 
 If you choose to use this option, create a `./p/i/.htaccess` file with a matching `.htpasswd` file.
 
-You can also use any authentication backend as long as your web server exposes the authenticated user through the `REMOTE_USER` variable.
+You can also use any authentication backend as long as your web server exposes the authenticated user through the `Remote-User` variable.
 
 By default, new users allowed by HTTP Basic Auth will automatically be created in FreshRSS the first time they log in.
 You can disable auto-registration of new users by setting `http_auth_auto_register` to `false` in the configuration file.
 When using auto-registration, you can optionally use the `http_auth_auto_register_email_field` to specify the name of a web server
 variable containing the email address of the authenticated user (e.g. `REMOTE_USER_EMAIL`).
 
+## External Authentication
+
+You may also use the `Remote-User` or `X-WebAuth-User` header to integrate with a your reverse-proxy’s authentication.
+
+To enable this feature, you need to add the IP range (in CIDR notation) of your trusted proxy in the `trusted_sources` configuration option.
+To allow only one IPv4, you can use a `/32` like this: `trusted_sources => [ '192.168.1.10/32' ]`.
+Likewise to allow only one IPv6, you can use a `/128` like this: `trusted_sources => [ '::1/128' ]`.
+
+WARNING: FreshRSS will trust any IP configured in the `trusted_sources` option, if your proxy isn’t properly secured, an attacker could simply attach this header and get admin access.
+
 ## No Authentication
 
 Not using authentication on your server is dangerous, as anyone with access to your server would be able to make changes as an admin.

+ 54 - 1
lib/lib_rss.php

@@ -554,15 +554,68 @@ function get_user_configuration($username) {
 	return $user_conf;
 }
 
+/**
+ * Converts an IP (v4 or v6) to a binary representation using inet_pton
+ *
+ * @param string $ip the IP to convert
+ * @return string a binary representation of the specified IP
+ */
+function ipToBits(string $ip): string {
+	$binaryip = '';
+	foreach (str_split(inet_pton($ip)) as $char) {
+		$binaryip .= str_pad(decbin(ord($char)), 8, '0', STR_PAD_LEFT);
+	}
+	return $binaryip;
+}
+
+/**
+ * Check if an ip belongs to the provided range (in CIDR format)
+ *
+ * @param string $ip the IP that we want to verify (ex: 192.168.16.1)
+ * @param string $range the range to check against (ex: 192.168.16.0/24)
+ * @return boolean true if the IP is in the range, otherwise false
+ */
+function checkCIDR(string $ip, string $range): bool {
+	$binary_ip = ipToBits($ip);
+	list($subnet, $mask_bits) = explode('/', $range);
+	$mask_bits = intval($mask_bits);
+	$binary_subnet = ipToBits($subnet);
+
+	$ip_net_bits = substr($binary_ip, 0, $mask_bits);
+	$subnet_bits = substr($binary_subnet, 0, $mask_bits);
+
+	return $ip_net_bits === $subnet_bits;
+}
+
+/**
+ * Check if the client is allowed to send unsafe headers
+ * This uses the REMOTE_ADDR header to determine the sender's IP
+ * and the configuration option "trusted_sources" to get an array of the authorized ranges
+ *
+ * @return boolean, true if the sender's IP is in one of the ranges defined in the configuration, else false
+ */
+function checkTrustedIP(): bool {
+	if (!empty($_SERVER['REMOTE_ADDR'])) {
+		foreach (FreshRSS_Context::$system_conf->trusted_sources as $cidr) {
+			if (checkCIDR($_SERVER['REMOTE_ADDR'], $cidr)) {
+				return true;
+			}
+		}
+	}
+	return false;
+}
+
 /**
  * @return string
  */
 function httpAuthUser() {
 	if (!empty($_SERVER['REMOTE_USER'])) {
 		return $_SERVER['REMOTE_USER'];
+	} elseif (!empty($_SERVER['HTTP_REMOTE_USER']) && checkTrustedIP()) {
+		return $_SERVER['HTTP_REMOTE_USER'];
 	} elseif (!empty($_SERVER['REDIRECT_REMOTE_USER'])) {
 		return $_SERVER['REDIRECT_REMOTE_USER'];
-	} elseif (!empty($_SERVER['HTTP_X_WEBAUTH_USER'])) {
+	} elseif (!empty($_SERVER['HTTP_X_WEBAUTH_USER']) && checkTrustedIP()) {
 		return $_SERVER['HTTP_X_WEBAUTH_USER'];
 	}
 	return '';