Changeset - 3d7ba590f6f5
[Not reviewed]
default
0 4 0
Mads Kiilerich (mads) - 5 years ago 2021-05-09 22:34:02
mads@kiilerich.com
Grafted from: 9355431c99ac
auth: only use X- headers instead of REMOTE_ADDR if explicitly told so in remote_addr_header

Before, X-Forwarded-For (and others) headers would *always* be trusted blindly,
also in setups without a proxy server. It would thus in some cases be
possible for users to fake their IP, and thus potentially be possible to bypass
IP restrictions configured in Kallithea.

Fixed by making it configurable which WSGI environment variable to use for the
remote address. Users can configure remote_addr_header to for example
HTTP_X_FORWARDED_FOR instead of using the default REMOTE_ADDR.

This change is a bit similar to what is going on in the https_fixup middleware,
but is doing a bit more of what for example is happening in similar code in
werkzeug/middleware/proxy_fix.py .
4 files changed with 18 insertions and 16 deletions:
0 comments (0 inline, 0 general)
development.ini
Show inline comments
 
@@ -102,24 +102,27 @@ cache_dir = %(here)s/data
 
index_dir = %(here)s/data/index
 

	
 
## uncomment and set this path to use archive download cache
 
archive_cache_dir = %(here)s/data/tarballcache
 

	
 
## change this to unique ID for security
 
#app_instance_uuid = VERY-SECRET
 
app_instance_uuid = development-not-secret
 

	
 
## cut off limit for large diffs (size in bytes)
 
cut_off_limit = 256000
 

	
 
## WSGI environment variable to get the IP address of the client (default REMOTE_ADDR)
 
#remote_addr_variable = HTTP_X_FORWARDED_FOR
 

	
 
## always pretend the client connected using HTTPS (default false)
 
#force_https = true
 

	
 
## use Strict-Transport-Security headers (default false)
 
#use_htsts = true
 

	
 
## number of commits stats will parse on each iteration
 
commit_parse_limit = 25
 

	
 
## Path to Python executable to be used for git hooks.
 
## This value will be written inside the git hook scripts as the text
 
## after '#!' (shebang). When empty or not defined, the value of
docs/setup.rst
Show inline comments
 
@@ -414,26 +414,30 @@ some of the basic properties of the conn
 
the HTTP level. The WSGI server will provide this information to Kallithea in
 
the "environment".
 

	
 
In some setups, a proxy server will take requests from users and forward
 
them to the actual Kallithea server. The proxy server will thus be the
 
immediate client of the Kallithea WSGI server, and Kallithea will basically see
 
it as such. To make sure Kallithea sees the request as it arrived from the
 
client to the proxy server, the proxy server must be configured to
 
somehow pass the original information on to Kallithea, and Kallithea must be
 
configured to pick that information up and trust it.
 

	
 
Kallithea will by default rely on its WSGI server to provide the IP of the
 
client in the WSGI environment as ``REMOTE_ADDR``, but it can also
 
get it from the ``X-Real-IP`` or ``X-Forwarded-For`` HTTP headers.
 
client in the WSGI environment as ``REMOTE_ADDR``, but it can be configured to
 
get it from an HTTP header that has been set by the proxy server. For
 
example, if the proxy server puts the client IP in the ``X-Forwarded-For``
 
HTTP header, set::
 

	
 
    remote_addr_variable = HTTP_X_FORWARDED_FOR
 

	
 
Kallithea will by default rely on finding the protocol (``http`` or ``https``)
 
in the WSGI environment as ``wsgi.url_scheme``. If the proxy server puts
 
the protocol of the client request in the ``X-Url-Scheme``,
 
``X-Forwarded-Scheme``, or ``X-Forwarded-Proto`` HTTP header,
 
Kallithea can be configured to trust these headers by setting::
 

	
 
    https_fixup = true
 

	
 

	
 
HTTPS support
 
-------------
kallithea/controllers/base.py
Show inline comments
 
@@ -72,38 +72,30 @@ def _filter_proxy(ip):
 
    first proxy, so if we have a proxy that is adding one IP address, we can
 
    only trust the rightmost address.
 
    """
 
    if ',' in ip:
 
        _ips = ip.split(',')
 
        _first_ip = _ips[-1].strip()
 
        log.debug('Got multiple IPs %s, using %s', ','.join(_ips), _first_ip)
 
        return _first_ip
 
    return ip
 

	
 

	
 
def get_ip_addr(environ):
 
    proxy_key = 'HTTP_X_REAL_IP'
 
    proxy_key2 = 'HTTP_X_FORWARDED_FOR'
 
    def_key = 'REMOTE_ADDR'
 

	
 
    ip = environ.get(proxy_key)
 
    if ip:
 
        return _filter_proxy(ip)
 

	
 
    ip = environ.get(proxy_key2)
 
    if ip:
 
        return _filter_proxy(ip)
 

	
 
    ip = environ.get(def_key, '0.0.0.0')
 
    return _filter_proxy(ip)
 
    """The web server will set REMOTE_ADDR to the unfakeable IP layer client IP address.
 
    If using a proxy server, make it possible to use another value, such as
 
    the X-Forwarded-For header, by setting `remote_addr_variable = HTTP_X_FORWARDED_FOR`.
 
    """
 
    remote_addr_variable = kallithea.CONFIG.get('remote_addr_variable', 'REMOTE_ADDR')
 
    return _filter_proxy(environ.get(remote_addr_variable, '0.0.0.0'))
 

	
 

	
 
def get_path_info(environ):
 
    """Return PATH_INFO from environ ... using tg.original_request if available.
 

	
 
    In Python 3 WSGI, PATH_INFO is a unicode str, but kind of contains encoded
 
    bytes. The code points are guaranteed to only use the lower 8 bit bits, and
 
    encoding the string with the 1:1 encoding latin1 will give the
 
    corresponding byte string ... which then can be decoded to proper unicode.
 
    """
 
    org_req = environ.get('tg.original_request')
 
    if org_req is not None:
kallithea/templates/ini/template.ini.mako
Show inline comments
 
@@ -165,24 +165,27 @@ static_files = true
 
cache_dir = %(here)s/data
 
index_dir = %(here)s/data/index
 

	
 
<%text>##</%text> uncomment and set this path to use archive download cache
 
archive_cache_dir = %(here)s/data/tarballcache
 

	
 
<%text>##</%text> change this to unique ID for security
 
app_instance_uuid = ${uuid()}
 

	
 
<%text>##</%text> cut off limit for large diffs (size in bytes)
 
cut_off_limit = 256000
 

	
 
<%text>##</%text> WSGI environment variable to get the IP address of the client (default REMOTE_ADDR)
 
#remote_addr_variable = HTTP_X_FORWARDED_FOR
 

	
 
<%text>##</%text> always pretend the client connected using HTTPS (default false)
 
#force_https = true
 

	
 
<%text>##</%text> use Strict-Transport-Security headers (default false)
 
#use_htsts = true
 

	
 
<%text>##</%text> number of commits stats will parse on each iteration
 
commit_parse_limit = 25
 

	
 
<%text>##</%text> Path to Python executable to be used for git hooks.
 
<%text>##</%text> This value will be written inside the git hook scripts as the text
 
<%text>##</%text> after '#!' (shebang). When empty or not defined, the value of
0 comments (0 inline, 0 general)