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
 
@@ -90,48 +90,51 @@ use = egg:kallithea
 
full_stack = true
 
static_files = true
 

	
 
## Internationalization (see setup documentation for details)
 
## By default, the languages requested by the browser are used if available, with English as default.
 
## Set i18n.enabled=false to disable automatic language choice.
 
#i18n.enabled = true
 
## To Force a language, set i18n.enabled=false and specify the language in i18n.lang.
 
## Valid values are the names of subdirectories in kallithea/i18n with a LC_MESSAGES/kallithea.mo
 
#i18n.lang = en
 

	
 
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
 
## 'sys.executable' at the time of installation of the git hooks is
 
## used, which is correct in many cases but for example not when using uwsgi.
 
## If you change this setting, you should reinstall the Git hooks via
 
## Admin > Settings > Remap and Rescan.
 
#git_hook_interpreter = /srv/kallithea/venv/bin/python3
 

	
 
## path to git executable
 
git_path = git
 

	
 
## git rev filter option, --all is the default filter, if you need to
 
## hide all refs in changelog switch this to --branches --tags
 
#git_rev_filter = --branches --tags
docs/setup.rst
Show inline comments
 
@@ -402,50 +402,54 @@ Extra options to the Celery worker can b
 
for more info.
 

	
 
.. note::
 
   Make sure you run this command from the same virtualenv, and with the same
 
   user that Kallithea runs.
 

	
 

	
 
Proxy setups
 
------------
 

	
 
When Kallithea is processing HTTP requests from a user, it will see and use
 
some of the basic properties of the connection, both at the TCP/IP level and at
 
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 will by default generate URLs based on the WSGI environment.
 

	
 
Alternatively, you can use some special configuration settings to control
 
directly which scheme/protocol Kallithea will use when generating URLs:
 

	
 
- With ``https_fixup = true``, the scheme will be taken from the
 
  ``X-Url-Scheme``, ``X-Forwarded-Scheme`` or ``X-Forwarded-Proto`` HTTP header
 
  (default ``http``).
 
- With ``force_https = true``, the scheme will be seen as ``https``.
 
- With ``use_htsts = true``, Kallithea will set ``Strict-Transport-Security`` when using https.
 

	
kallithea/controllers/base.py
Show inline comments
 
@@ -60,62 +60,54 @@ log = logging.getLogger(__name__)
 

	
 
def render(template_path):
 
    return render_template({'url': url}, 'mako', template_path)
 

	
 

	
 
def _filter_proxy(ip):
 
    """
 
    HTTP_X_FORWARDED_FOR headers can have multiple IP addresses, with the
 
    leftmost being the original client. Each proxy that is forwarding the
 
    request will usually add the IP address it sees the request coming from.
 

	
 
    The client might have provided a fake leftmost value before hitting the
 
    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:
 
        environ = org_req.environ
 
    return safe_str(environ['PATH_INFO'].encode('latin1'))
 

	
 

	
 
def log_in_user(user, remember, is_external_auth, ip_addr):
 
    """
 
    Log a `User` in and update session and cookies. If `remember` is True,
 
    the session cookie is set to expire in a year; otherwise, it expires at
 
    the end of the browser session.
 

	
 
    Returns populated `AuthUser` object.
 
    """
kallithea/templates/ini/template.ini.mako
Show inline comments
 
@@ -153,48 +153,51 @@ use = egg:kallithea
 

	
 
full_stack = true
 
static_files = true
 

	
 
<%text>##</%text> Internationalization (see setup documentation for details)
 
<%text>##</%text> By default, the languages requested by the browser are used if available, with English as default.
 
<%text>##</%text> Set i18n.enabled=false to disable automatic language choice.
 
#i18n.enabled = true
 
<%text>##</%text> To Force a language, set i18n.enabled=false and specify the language in i18n.lang.
 
<%text>##</%text> Valid values are the names of subdirectories in kallithea/i18n with a LC_MESSAGES/kallithea.mo
 
#i18n.lang = en
 

	
 
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
 
<%text>##</%text> 'sys.executable' at the time of installation of the git hooks is
 
<%text>##</%text> used, which is correct in many cases but for example not when using uwsgi.
 
<%text>##</%text> If you change this setting, you should reinstall the Git hooks via
 
<%text>##</%text> Admin > Settings > Remap and Rescan.
 
#git_hook_interpreter = /srv/kallithea/venv/bin/python3
 
%if git_hook_interpreter:
 
git_hook_interpreter = ${git_hook_interpreter}
 
%endif
 

	
 
<%text>##</%text> path to git executable
 
git_path = git
 

	
0 comments (0 inline, 0 general)