changeset 8169:627c5d6a0551

allow roundup-server to log real client IP behind reverse proxy added -P flag to roundup-server to log client address from X-Forwarded-For reverse proxy header rather than connecting address. This logs the actual client address when roundup-server is run behind a reverse proxy. It also appends a '+' sign to the logged address/name. This makes correlating reverse proxy logs to roundup logs much easier by propagating the IP address. Also added documentation for -D flag that was undocumented.
author John Rouillard <rouilj@ieee.org>
date Sun, 01 Dec 2024 17:38:15 -0500
parents 3f0f4746dc7e
children bf4a5bd5aa9f
files CHANGES.txt roundup/scripts/roundup_server.py share/man/man1/roundup-server.1
diffstat 3 files changed, 51 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES.txt	Tue Nov 26 17:11:13 2024 -0500
+++ b/CHANGES.txt	Sun Dec 01 17:38:15 2024 -0500
@@ -47,6 +47,11 @@
   filtering in the database.  (Ralf Schlatterbeck)
 - issue2551370 - mark roundup session cookie with __Secure-
   prefix. (John Rouillard)
+- add -P flag to roundup-server to log client address from
+  X-Forwarded-For reverse proxy header rather than connecting
+  address. This logs the actual client address when
+  roundup-server is run behind a reverse proxy. It also appends a
+  + sign to the logged address/name. (John Rouillard)
 
 2024-07-13 2.4.0
 
--- a/roundup/scripts/roundup_server.py	Tue Nov 26 17:11:13 2024 -0500
+++ b/roundup/scripts/roundup_server.py	Sun Dec 01 17:38:15 2024 -0500
@@ -26,6 +26,7 @@
 import io
 import logging
 import os
+import re
 import socket
 import sys     # modify sys.path when running in source tree
 import time
@@ -534,11 +535,27 @@
         tracker.Client(tracker, self, env).main()
 
     def address_string(self):
+        """Get IP address of client from:
+               left most element of X-Forwarded-For header element if set
+               client ip address otherwise.
+           if returned string is from X-Forwarded-For append + to string.
+        """
+        from_forwarded_header=""
+        forwarded_for = None
+        if 'X-FORWARDED-FOR' in self.headers:
+            forwarded_for = re.split(r'[,\s]',
+                                     self.headers['X-FORWARDED-FOR'],
+                                     maxsplit=1)[0]
+            from_forwarded_header="+"
         if self.LOG_IPADDRESS:
-            return self.client_address[0]
+            return "%s%s" % (forwarded_for or self.client_address[0],
+                             from_forwarded_header)
         else:
-            host, port = self.client_address
-            return socket.getfqdn(host)
+            if forwarded_for:
+                host = forwarded_for
+            else:
+                host, port = self.client_address
+            return "%s%s" % (socket.getfqdn(host), from_forwarded_header)
 
     def log_message(self, format, *args):
         ''' Try to *safely* log to stderr.
@@ -547,7 +564,7 @@
             logger = logging.getLogger('roundup.http')
 
             logger.info("%s - - [%s] %s" %
-                        (self.client_address[0],
+                        (self.address_string(),
                          self.log_date_time_string(),
                          format % args))
         else:
@@ -680,6 +697,11 @@
                 "If set to yes the python logging module is used with "
                 "qualname\n'roundup.http'. Otherwise logging is done to "
                 "stderr or the file\nspecified using the -l/logfile option."),
+            (configuration.BooleanOption, "log_proxy_header", "no",
+                "Use first element of reverse proxy header X-Forwarded-For "
+                "as client IP address.\nThis appends a '+' sign to the logged "
+                "host ip/name. Use only if server is\naccessible only via "
+                "trusted reverse proxy."),
             (configuration.NullableFilePathOption, "pidfile", "",
                 "File to which the server records "
                 "the process id of the daemon.\n"
@@ -734,6 +756,7 @@
         "multiprocess": "t:",
         "template": "i:",
         "loghttpvialogger": 'L',
+        "log_proxy_header": 'P',
         "ssl": "s",
         "pem": "e:",
         "include_headers": "I:",
@@ -952,7 +975,8 @@
  -g <GID>      runs the Roundup web server as this GID
  -d <PIDfile>  run the server in the background and write the server's PID
                to the file indicated by PIDfile. The -l option *must* be
-               specified if -d is used.'''
+               specified if -d is used.
+ -D            run the server in the foreground even when -d is used.'''
     if message:
         message += '\n\n'
     print(_('''\n%(message)sUsage: roundup-server [options] [name=tracker home]*
@@ -974,6 +998,9 @@
  -m <children> maximum number of children to spawn in fork multiprocess mode
  -s            enable SSL
  -L            http request logging uses python logging (roundup.http)
+ -P            log client address/name using reverse proxy X-Forwarded-For
+               header and not the connection IP (which is the reverse proxy).
+               Appends a '+' sign to the logged address/name.
  -e <fname>    PEM file containing SSL key and certificate
  -t <mode>     multiprocess mode (default: %(mp_def)s).
                Allowed values: %(mp_types)s.
--- a/share/man/man1/roundup-server.1	Tue Nov 26 17:11:13 2024 -0500
+++ b/share/man/man1/roundup-server.1	Sun Dec 01 17:38:15 2024 -0500
@@ -20,6 +20,9 @@
 file indicated by PIDfile. The -l (or -L) option \fBmust\fP be
 specified if -d is used.
 .TP
+\fB-D\fP
+Run the server in the foreground even if -d is used.
+.TP
 \fB-t\fP \fBfork|thread|debug|none\fP
 Control multi-process mode. \fBdebug\fP and \fBnone\fP are always
 available. If an invalid mode is specified the server starts in
@@ -33,6 +36,17 @@
 Sets a filename to log to (instead of stdout). This is required if the -d
 option is used.
 .TP
+\fB-P\fP
+If a reverse proxy is used in front of the roundup-server, the server
+will log the ip address of the proxy, not the client browser. Using -P
+logs the left most entry in the X-Forwarded-For http header as the
+IP address of the client. This address will be logged or resolved to a
+hostname (with \fB-N\fP) and a '+' character will be appended.
+\fB-P\fP should only be used when the
+roundup server is accessible only from trusted proxy hosts. See:
+https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For
+for details and warnings about using the X-Forwarded-For header.
+.TP
 \fB-L\fP
 Have the server log using the Python logger with key roundup.http.
 .TP

Roundup Issue Tracker: http://roundup-tracker.org/