1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/e34bf92384b6/
Changeset: e34bf92384b6
User: jmchilton
Date: 2015-01-05 15:05:46+00:00
Summary: Attempt to fix bug introduced in 08f8850853d004bf8a456a147436a669.
Thanks to Nicola for the bug report (https://bitbucket.org/galaxy/galaxy-central/commits/08f8850853d004bf8a456a1…).
Affected #: 1 file
diff -r 4e60046a38c4bf79c77e5232856208b7e52dc162 -r e34bf92384b6a6722657d83833fb52268865126d lib/galaxy/tools/toolbox/base.py
--- a/lib/galaxy/tools/toolbox/base.py
+++ b/lib/galaxy/tools/toolbox/base.py
@@ -483,8 +483,8 @@
return[ self._tools_by_id[ tool_id ] ]
return None
- def has_tool( self, tool_id, exact=False ):
- return self.get_tool( tool_id, exact=exact ) is not None
+ def has_tool( self, tool_id, tool_version=None, exact=False ):
+ return self.get_tool( tool_id, tool_version=tool_version, exact=exact ) is not None
def get_tool_id( self, tool_id ):
""" Take a tool id (potentially from a different Galaxy instance or that
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/4e60046a38c4/
Changeset: 4e60046a38c4
User: dannon
Date: 2015-01-05 14:06:01+00:00
Summary: Slight modification to prevent unintentional (safe and unused, but still) reassignment of parameter 'value' in loop.
Affected #: 1 file
diff -r f55708c39129ab2b293edb7ec54faa85506d2800 -r 4e60046a38c4bf79c77e5232856208b7e52dc162 lib/tool_shed/util/web_util.py
--- a/lib/tool_shed/util/web_util.py
+++ b/lib/tool_shed/util/web_util.py
@@ -4,7 +4,7 @@
ALLOWED_MAP = dict(map(lambda x: (x, raw_escape(x)), ALLOWED_ELEMENTS))
-def escape( value ):
+def escape( string ):
""" A tool shed variant of markupsafe.escape that allows a select few
HTML elements that are repeatedly used in messages created deep
in the toolshed components. Ideally abstract things would be produced
@@ -14,7 +14,7 @@
>>> escape("A <b>repo</b>")
u'A <b>repo</b>'
"""
- escaped = str( raw_escape( value ) )
+ escaped = str( raw_escape( string ) )
# Unescape few selected tags.
for key, value in ALLOWED_MAP.iteritems():
escaped = escaped.replace(value, key)
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/dff4071c13f9/
Changeset: dff4071c13f9
Branch: stable
User: natefoo
Date: 2015-01-05 14:00:18+00:00
Summary: Update tag latest_2014.10.06 for changeset 793d9cd5f9de
Affected #: 1 file
diff -r 793d9cd5f9dec66ceafb36bc38f53453457056e6 -r dff4071c13f9b2908556583227766c65fcfb8a29 .hgtags
--- a/.hgtags
+++ b/.hgtags
@@ -20,4 +20,4 @@
ca45b78adb4152fc6e7395514d46eba6b7d0b838 release_2014.08.11
548ab24667d6206780237bd807f7d857a484c461 latest_2014.08.11
2092948937ac30ef82f71463a235c66d34987088 release_2014.10.06
-5834b1066462dd219f67c1c3cbd77c78e7cf3a6c latest_2014.10.06
+793d9cd5f9dec66ceafb36bc38f53453457056e6 latest_2014.10.06
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/793d9cd5f9de/
Changeset: 793d9cd5f9de
Branch: stable
User: jmchilton
Date: 2014-12-27 22:30:59+00:00
Summary: Fixes for over escaping in c2bed0a.
Fixes dozens of tool functional tests.
Affected #: 9 files
diff -r 7197e949d3ab990ceb76c193ebc940d90aa04cad -r 793d9cd5f9dec66ceafb36bc38f53453457056e6 lib/galaxy/web/base/controllers/admin.py
--- a/lib/galaxy/web/base/controllers/admin.py
+++ b/lib/galaxy/web/base/controllers/admin.py
@@ -7,7 +7,7 @@
from galaxy.web.form_builder import CheckboxField
from string import punctuation as PUNCTUATION
import galaxy.queue_worker
-from markupsafe import escape
+from tool_shed.util.web_util import escape
from tool_shed.util import shed_util_common as suc
diff -r 7197e949d3ab990ceb76c193ebc940d90aa04cad -r 793d9cd5f9dec66ceafb36bc38f53453457056e6 lib/galaxy/webapps/galaxy/controllers/admin.py
--- a/lib/galaxy/webapps/galaxy/controllers/admin.py
+++ b/lib/galaxy/webapps/galaxy/controllers/admin.py
@@ -17,7 +17,7 @@
from galaxy.web.params import QuotaParamParser
from tool_shed.util import common_util
from tool_shed.util import encoding_util
-from markupsafe import escape
+from tool_shed.util.web_util import escape
log = logging.getLogger( __name__ )
diff -r 7197e949d3ab990ceb76c193ebc940d90aa04cad -r 793d9cd5f9dec66ceafb36bc38f53453457056e6 lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
--- a/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
+++ b/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
@@ -8,9 +8,9 @@
from galaxy.web.form_builder import CheckboxField
from galaxy.util import json
from galaxy.model.orm import or_
-from markupsafe import escape
import tool_shed.repository_types.util as rt_util
+from tool_shed.util.web_util import escape
from tool_shed.util import common_util
from tool_shed.util import encoding_util
diff -r 7197e949d3ab990ceb76c193ebc940d90aa04cad -r 793d9cd5f9dec66ceafb36bc38f53453457056e6 lib/galaxy/webapps/tool_shed/controllers/admin.py
--- a/lib/galaxy/webapps/tool_shed/controllers/admin.py
+++ b/lib/galaxy/webapps/tool_shed/controllers/admin.py
@@ -3,7 +3,7 @@
from galaxy import util
from galaxy.util import inflector
from galaxy import web
-from markupsafe import escape
+from tool_shed.util.web_util import escape
from galaxy.web.base.controller import BaseUIController
from galaxy.web.base.controllers.admin import Admin
diff -r 7197e949d3ab990ceb76c193ebc940d90aa04cad -r 793d9cd5f9dec66ceafb36bc38f53453457056e6 lib/galaxy/webapps/tool_shed/controllers/repository.py
--- a/lib/galaxy/webapps/tool_shed/controllers/repository.py
+++ b/lib/galaxy/webapps/tool_shed/controllers/repository.py
@@ -6,7 +6,6 @@
from time import strftime
from datetime import date
from datetime import datetime
-from markupsafe import escape
from galaxy import util
from galaxy import web
@@ -19,6 +18,7 @@
from tool_shed.capsule import capsule_manager
from tool_shed.dependencies.repository import relation_builder
+from tool_shed.util.web_util import escape
from tool_shed.galaxy_install import dependency_display
from tool_shed.metadata import repository_metadata_manager
from tool_shed.utility_containers import ToolShedUtilityContainerManager
diff -r 7197e949d3ab990ceb76c193ebc940d90aa04cad -r 793d9cd5f9dec66ceafb36bc38f53453457056e6 lib/galaxy/webapps/tool_shed/controllers/repository_review.py
--- a/lib/galaxy/webapps/tool_shed/controllers/repository_review.py
+++ b/lib/galaxy/webapps/tool_shed/controllers/repository_review.py
@@ -2,7 +2,7 @@
import os
from sqlalchemy.sql.expression import func
-from markupsafe import escape
+from tool_shed.util.web_util import escape
from galaxy import util
from galaxy import web
diff -r 7197e949d3ab990ceb76c193ebc940d90aa04cad -r 793d9cd5f9dec66ceafb36bc38f53453457056e6 lib/galaxy/webapps/tool_shed/controllers/upload.py
--- a/lib/galaxy/webapps/tool_shed/controllers/upload.py
+++ b/lib/galaxy/webapps/tool_shed/controllers/upload.py
@@ -9,7 +9,7 @@
from galaxy import web
from galaxy.datatypes import checkers
from galaxy.web.base.controller import BaseUIController
-from markupsafe import escape
+from tool_shed.util.web_util import escape
from tool_shed.dependencies import attribute_handlers
from tool_shed.galaxy_install import dependency_display
diff -r 7197e949d3ab990ceb76c193ebc940d90aa04cad -r 793d9cd5f9dec66ceafb36bc38f53453457056e6 lib/tool_shed/util/repository_util.py
--- a/lib/tool_shed/util/repository_util.py
+++ b/lib/tool_shed/util/repository_util.py
@@ -7,7 +7,7 @@
from galaxy import web
from galaxy.web.form_builder import build_select_field
from galaxy.webapps.tool_shed.model import directory_hash_id
-from markupsafe import escape
+from tool_shed.util.web_util import escape
from tool_shed.dependencies.repository import relation_builder
diff -r 7197e949d3ab990ceb76c193ebc940d90aa04cad -r 793d9cd5f9dec66ceafb36bc38f53453457056e6 lib/tool_shed/util/web_util.py
--- /dev/null
+++ b/lib/tool_shed/util/web_util.py
@@ -0,0 +1,21 @@
+from markupsafe import escape as raw_escape
+
+ALLOWED_ELEMENTS = ["<b>", "</b>", "<br/>"]
+ALLOWED_MAP = dict(map(lambda x: (x, raw_escape(x)), ALLOWED_ELEMENTS))
+
+
+def escape( value ):
+ """ A tool shed variant of markupsafe.escape that allows a select few
+ HTML elements that are repeatedly used in messages created deep
+ in the toolshed components. Ideally abstract things would be produced
+ in these components and messages in the views or client side - this is
+ what should be worked toward - but for now - we have this hack.
+
+ >>> escape("A <b>repo</b>")
+ u'A <b>repo</b>'
+ """
+ escaped = str( raw_escape( value ) )
+ # Unescape few selected tags.
+ for key, value in ALLOWED_MAP.iteritems():
+ escaped = escaped.replace(value, key)
+ return escaped
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/ac1859e27eb0/
Changeset: ac1859e27eb0
User: jmchilton
Date: 2015-01-01 19:16:50+00:00
Summary: Merged in erasche2/galaxy-central2 (pull request #619)
Allow for shared secret between Galaxy and upstream proxies
Affected #: 9 files
diff -r 84d5a72790735ae4da03f27735c52182bb925d9b -r ac1859e27eb0217ae5dabd7495a3b9314f65a1f7 config/galaxy.ini.sample
--- a/config/galaxy.ini.sample
+++ b/config/galaxy.ini.sample
@@ -713,6 +713,13 @@
# *not* include 'HTTP_' at the beginning of the header name.
#remote_user_header = HTTP_REMOTE_USER
+# If use_remote_user is enabled, anyone who can log in to the Galaxy host may
+# impersonate any other user by simply sending the appropriate header. Thus a
+# secret shared between the upstream proxy server, and Galaxy is required.
+# If anyone other than the Galaxy user is using the server, then apache/nginx should
+# pass a value in the header 'GX_SECRET' that is identical the one below
+#remote_user_secret = USING THE DEFAULT IS NOT SECURE!
+
# If use_remote_user is enabled, you can set this to a URL that will log your
# users out.
#remote_user_logout_href = None
diff -r 84d5a72790735ae4da03f27735c52182bb925d9b -r ac1859e27eb0217ae5dabd7495a3b9314f65a1f7 config/tool_shed.ini.sample
--- a/config/tool_shed.ini.sample
+++ b/config/tool_shed.ini.sample
@@ -67,6 +67,13 @@
# https://wiki.galaxyproject.org/Admin/Config/ApacheProxy
#use_remote_user = False
+# If use_remote_user is enabled, anyone who can log in to the Galaxy host may
+# impersonate any other user by simply sending the appropriate header. Thus a
+# secret shared between the upstream proxy server, and Galaxy is required.
+# If anyone other than the Galaxy user is using the server, then apache/nginx should
+# pass a value in the header 'GX_SECRET' that is identical the one below
+#remote_user_secret = changethisinproductiontoo
+
# Configuration for debugging middleware
debug = true
use_lint = false
diff -r 84d5a72790735ae4da03f27735c52182bb925d9b -r ac1859e27eb0217ae5dabd7495a3b9314f65a1f7 lib/galaxy/config.py
--- a/lib/galaxy/config.py
+++ b/lib/galaxy/config.py
@@ -133,6 +133,7 @@
self.remote_user_maildomain = kwargs.get( "remote_user_maildomain", None )
self.remote_user_header = kwargs.get( "remote_user_header", 'HTTP_REMOTE_USER' )
self.remote_user_logout_href = kwargs.get( "remote_user_logout_href", None )
+ self.remote_user_secret = kwargs.get( "remote_user_secret", None )
self.require_login = string_as_bool( kwargs.get( "require_login", "False" ) )
self.allow_user_creation = string_as_bool( kwargs.get( "allow_user_creation", "True" ) )
self.allow_user_deletion = string_as_bool( kwargs.get( "allow_user_deletion", "False" ) )
diff -r 84d5a72790735ae4da03f27735c52182bb925d9b -r ac1859e27eb0217ae5dabd7495a3b9314f65a1f7 lib/galaxy/util/__init__.py
--- a/lib/galaxy/util/__init__.py
+++ b/lib/galaxy/util/__init__.py
@@ -1119,6 +1119,8 @@
def safe_str_cmp(a, b):
+ """safely compare two strings in a timing-attack-resistant manner
+ """
if len(a) != len(b):
return False
rv = 0
diff -r 84d5a72790735ae4da03f27735c52182bb925d9b -r ac1859e27eb0217ae5dabd7495a3b9314f65a1f7 lib/galaxy/web/framework/middleware/remoteuser.py
--- a/lib/galaxy/web/framework/middleware/remoteuser.py
+++ b/lib/galaxy/web/framework/middleware/remoteuser.py
@@ -3,6 +3,7 @@
"""
import socket
+from galaxy.util import safe_str_cmp
errorpage = """
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
@@ -37,12 +38,13 @@
class RemoteUser( object ):
- def __init__( self, app, maildomain=None, display_servers=None, admin_users=None, remote_user_header=None ):
+ def __init__( self, app, maildomain=None, display_servers=None, admin_users=None, remote_user_header=None, remote_user_secret_header=None ):
self.app = app
self.maildomain = maildomain
self.display_servers = display_servers or []
self.admin_users = admin_users or []
self.remote_user_header = remote_user_header or 'HTTP_REMOTE_USER'
+ self.config_secret_header = remote_user_secret_header
def __call__( self, environ, start_response ):
# Allow display servers
@@ -59,6 +61,34 @@
# Rewrite* method for passing REMOTE_USER and a user is
# un-authenticated. Any other possible values need to go here as well.
path_info = environ.get('PATH_INFO', '')
+
+ # If the secret header is enabled, we expect upstream to send along some key
+ # in HTTP_GX_SECRET, so we'll need to compare that here to the correct value
+ #
+ # This is not an ideal location for this function. The reason being
+ # that because this check is done BEFORE the REMOTE_USER check, it is
+ # possible to attack the GX_SECRET key without having correct
+ # credentials. However, that's why it's not "ideal", but it is "good
+ # enough". The only users able to exploit this are ones with access to
+ # the local system (unless Galaxy is listening on 0.0.0.0....). It
+ # seems improbable that an attacker with access to the server hosting
+ # Galaxy would not have access to Galaxy itself, and be attempting to
+ # attack the system
+ if self.config_secret_header is not None:
+ if not safe_str_cmp(environ.get('HTTP_GX_SECRET'), self.config_secret_header):
+ title = "Access to Galaxy is denied"
+ message = """
+ Galaxy is configured to authenticate users via an external
+ method (such as HTTP authentication in Apache), but an
+ incorrect shared secret key was provided by the
+ upstream (proxy) server.</p>
+ <p>Please contact your local Galaxy administrator. The
+ variable <code>remote_user_secret</code> and
+ <code>GX_SECRET</code> header must be set before you may
+ access Galaxy.
+ """
+ return self.error( start_response, title, message )
+
if not environ.get(self.remote_user_header, '(null)').startswith('(null)'):
if not environ[ self.remote_user_header ].count( '@' ):
if self.maildomain is not None:
diff -r 84d5a72790735ae4da03f27735c52182bb925d9b -r ac1859e27eb0217ae5dabd7495a3b9314f65a1f7 lib/galaxy/webapps/galaxy/buildapp.py
--- a/lib/galaxy/webapps/galaxy/buildapp.py
+++ b/lib/galaxy/webapps/galaxy/buildapp.py
@@ -503,8 +503,8 @@
app = RemoteUser( app, maildomain = conf.get( 'remote_user_maildomain', None ),
display_servers = util.listify( conf.get( 'display_servers', '' ) ),
admin_users = conf.get( 'admin_users', '' ).split( ',' ),
- remote_user_header = conf.get( 'remote_user_header', 'HTTP_REMOTE_USER' ) )
- log.debug( "Enabling 'remote user' middleware" )
+ remote_user_header = conf.get( 'remote_user_header', 'HTTP_REMOTE_USER' ),
+ remote_user_secret_header = conf.get('remote_user_secret', None) )
# The recursive middleware allows for including requests in other
# requests or forwarding of requests, all on the server side.
if asbool(conf.get('use_recursive', True)):
diff -r 84d5a72790735ae4da03f27735c52182bb925d9b -r ac1859e27eb0217ae5dabd7495a3b9314f65a1f7 lib/galaxy/webapps/tool_shed/buildapp.py
--- a/lib/galaxy/webapps/tool_shed/buildapp.py
+++ b/lib/galaxy/webapps/tool_shed/buildapp.py
@@ -157,7 +157,8 @@
from galaxy.webapps.tool_shed.framework.middleware.remoteuser import RemoteUser
app = RemoteUser( app, maildomain = conf.get( 'remote_user_maildomain', None ),
display_servers = util.listify( conf.get( 'display_servers', '' ) ),
- admin_users = conf.get( 'admin_users', '' ).split( ',' ) )
+ admin_users = conf.get( 'admin_users', '' ).split( ',' ),
+ remote_user_secret_header = conf.get('remote_user_secret', None) )
log.debug( "Enabling 'remote user' middleware" )
# The recursive middleware allows for including requests in other
# requests or forwarding of requests, all on the server side.
diff -r 84d5a72790735ae4da03f27735c52182bb925d9b -r ac1859e27eb0217ae5dabd7495a3b9314f65a1f7 lib/galaxy/webapps/tool_shed/config.py
--- a/lib/galaxy/webapps/tool_shed/config.py
+++ b/lib/galaxy/webapps/tool_shed/config.py
@@ -73,6 +73,7 @@
self.remote_user_maildomain = kwargs.get( "remote_user_maildomain", None )
self.remote_user_header = kwargs.get( "remote_user_header", 'HTTP_REMOTE_USER' )
self.remote_user_logout_href = kwargs.get( "remote_user_logout_href", None )
+ self.remote_user_secret = kwargs.get( "remote_user_secret", None )
self.require_login = string_as_bool( kwargs.get( "require_login", "False" ) )
self.allow_user_creation = string_as_bool( kwargs.get( "allow_user_creation", "True" ) )
self.allow_user_deletion = string_as_bool( kwargs.get( "allow_user_deletion", "False" ) )
diff -r 84d5a72790735ae4da03f27735c52182bb925d9b -r ac1859e27eb0217ae5dabd7495a3b9314f65a1f7 lib/galaxy/webapps/tool_shed/framework/middleware/remoteuser.py
--- a/lib/galaxy/webapps/tool_shed/framework/middleware/remoteuser.py
+++ b/lib/galaxy/webapps/tool_shed/framework/middleware/remoteuser.py
@@ -3,6 +3,7 @@
"""
import socket
+from galaxy.util import safe_str_cmp
errorpage = """
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
@@ -36,11 +37,13 @@
"""
class RemoteUser( object ):
- def __init__( self, app, maildomain=None, display_servers=None, admin_users=None ):
+ def __init__( self, app, maildomain=None, display_servers=None, admin_users=None, remote_user_secret_header=None ):
self.app = app
self.maildomain = maildomain
self.display_servers = display_servers or []
self.admin_users = admin_users or []
+ self.config_secret_header = remote_user_secret_header
+
def __call__( self, environ, start_response ):
environ[ 'webapp' ] = 'tool_shed'
# Allow display servers
@@ -53,6 +56,34 @@
if host in self.display_servers:
environ[ 'HTTP_REMOTE_USER' ] = 'remote_display_server@%s' % ( self.maildomain or 'example.org' )
return self.app( environ, start_response )
+
+ # If the secret header is enabled, we expect upstream to send along some key
+ # in HTTP_GX_SECRET, so we'll need to compare that here to the correct value
+ #
+ # This is not an ideal location for this function. The reason being
+ # that because this check is done BEFORE the REMOTE_USER check, it is
+ # possible to attack the GX_SECRET key without having correct
+ # credentials. However, that's why it's not "ideal", but it is "good
+ # enough". The only users able to exploit this are ones with access to
+ # the local system (unless Galaxy is listening on 0.0.0.0....). It
+ # seems improbable that an attacker with access to the server hosting
+ # Galaxy would not have access to Galaxy itself, and be attempting to
+ # attack the system
+ if self.config_secret_header is not None:
+ if not safe_str_cmp(environ.get('HTTP_GX_SECRET'), self.config_secret_header):
+ title = "Access to Galaxy is denied"
+ message = """
+ Galaxy is configured to authenticate users via an external
+ method (such as HTTP authentication in Apache), but an
+ incorrect shared secret key was provided by the
+ upstream (proxy) server.</p>
+ <p>Please contact your local Galaxy administrator. The
+ variable <code>remote_user_secret</code> and
+ <code>GX_SECRET</code> header must be set before you may
+ access Galaxy.
+ """
+ return self.error( start_response, title, message )
+
# Apache sets REMOTE_USER to the string '(null)' when using the Rewrite* method for passing REMOTE_USER and a user is
# un-authenticated. Any other possible values need to go here as well.
path_info = environ.get('PATH_INFO', '')
@@ -88,6 +119,7 @@
<p>Contact your local Galaxy tool shed administrator.
"""
return self.error( start_response, title, message )
+
def error( self, start_response, title="Access denied", message="Contact your local Galaxy tool shed administrator." ):
start_response( '403 Forbidden', [('Content-type', 'text/html')] )
return [errorpage % (title, message)]
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
3 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/7a889340aba7/
Changeset: 7a889340aba7
User: erasche2
Date: 2014-12-17 21:25:07+00:00
Summary: Allow for shared secret between Galaxy and upstream proxies
This prevents an impersonation attack available on Galaxies with command line
users and REMOTE_USER authentication. It is not uncommon to run Galaxy on a
"lab server" (or similar), where there are command line users in addition to
the Galaxy service. With REMOTE_USER authentication, Galaxy blindly (and
correctly) trusts the upstream value of REMOTE_USER, enabling such attacks as:
curl -H "REMOTE_USER: admin.user(a)domain.edu" http://localhost:8000/galaxy/
This changest adds a shared secret between Galaxy and the upstream proxy,
thereby preventing that attack. Only requests from the upstream proxy with the
correct secret key are recognised, everything else is tossed out with a 403
Forbidden.
This was implemented as part of the REMOTE_USER stack as the situation only
applies if that middleware is in use.
Affected #: 9 files
diff -r 5e5c0cf930bb96bf8af0bd19daad1a999b40c374 -r 7a889340aba7e078d31963aa99139c7ddaea07c2 config/galaxy.ini.sample
--- a/config/galaxy.ini.sample
+++ b/config/galaxy.ini.sample
@@ -713,6 +713,13 @@
# *not* include 'HTTP_' at the beginning of the header name.
#remote_user_header = HTTP_REMOTE_USER
+# If use_remote_user is enabled, anyone who can log in to the Galaxy host may
+# impersonate any other user by simply sending the appropriate header. Thus a
+# secret shared between the upstream proxy server, and Galaxy is required.
+# If anyone other than the Galaxy user is using the server, then apache/nginx should
+# pass a value in the header 'GX_SECRET' that is identical the one below
+#remote_user_secret = USING THE DEFAULT IS NOT SECURE!
+
# If use_remote_user is enabled, you can set this to a URL that will log your
# users out.
#remote_user_logout_href = None
diff -r 5e5c0cf930bb96bf8af0bd19daad1a999b40c374 -r 7a889340aba7e078d31963aa99139c7ddaea07c2 config/tool_shed.ini.sample
--- a/config/tool_shed.ini.sample
+++ b/config/tool_shed.ini.sample
@@ -67,6 +67,13 @@
# https://wiki.galaxyproject.org/Admin/Config/ApacheProxy
#use_remote_user = False
+# If use_remote_user is enabled, anyone who can log in to the Galaxy host may
+# impersonate any other user by simply sending the appropriate header. Thus a
+# secret shared between the upstream proxy server, and Galaxy is required.
+# If anyone other than the Galaxy user is using the server, then apache/nginx should
+# pass a value in the header 'GX_SECRET' that is identical the one below
+#remote_user_secret = changethisinproductiontoo
+
# Configuration for debugging middleware
debug = true
use_lint = false
diff -r 5e5c0cf930bb96bf8af0bd19daad1a999b40c374 -r 7a889340aba7e078d31963aa99139c7ddaea07c2 lib/galaxy/config.py
--- a/lib/galaxy/config.py
+++ b/lib/galaxy/config.py
@@ -132,6 +132,7 @@
self.remote_user_maildomain = kwargs.get( "remote_user_maildomain", None )
self.remote_user_header = kwargs.get( "remote_user_header", 'HTTP_REMOTE_USER' )
self.remote_user_logout_href = kwargs.get( "remote_user_logout_href", None )
+ self.remote_user_secret = kwargs.get( "remote_user_secret", None )
self.require_login = string_as_bool( kwargs.get( "require_login", "False" ) )
self.allow_user_creation = string_as_bool( kwargs.get( "allow_user_creation", "True" ) )
self.allow_user_deletion = string_as_bool( kwargs.get( "allow_user_deletion", "False" ) )
diff -r 5e5c0cf930bb96bf8af0bd19daad1a999b40c374 -r 7a889340aba7e078d31963aa99139c7ddaea07c2 lib/galaxy/util/__init__.py
--- a/lib/galaxy/util/__init__.py
+++ b/lib/galaxy/util/__init__.py
@@ -1119,6 +1119,8 @@
def safe_str_cmp(a, b):
+ """safely compare two strings in a timing-attack-resistant manner
+ """
if len(a) != len(b):
return False
rv = 0
diff -r 5e5c0cf930bb96bf8af0bd19daad1a999b40c374 -r 7a889340aba7e078d31963aa99139c7ddaea07c2 lib/galaxy/web/framework/middleware/remoteuser.py
--- a/lib/galaxy/web/framework/middleware/remoteuser.py
+++ b/lib/galaxy/web/framework/middleware/remoteuser.py
@@ -3,6 +3,7 @@
"""
import socket
+from galaxy.util import safe_str_cmp
errorpage = """
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
@@ -37,12 +38,13 @@
class RemoteUser( object ):
- def __init__( self, app, maildomain=None, display_servers=None, admin_users=None, remote_user_header=None ):
+ def __init__( self, app, maildomain=None, display_servers=None, admin_users=None, remote_user_header=None, remote_user_secret_header=None ):
self.app = app
self.maildomain = maildomain
self.display_servers = display_servers or []
self.admin_users = admin_users or []
self.remote_user_header = remote_user_header or 'HTTP_REMOTE_USER'
+ self.config_secret_header = remote_user_secret_header
def __call__( self, environ, start_response ):
# Allow display servers
@@ -59,6 +61,34 @@
# Rewrite* method for passing REMOTE_USER and a user is
# un-authenticated. Any other possible values need to go here as well.
path_info = environ.get('PATH_INFO', '')
+
+ # If the secret header is enabled, we expect upstream to send along some key
+ # in HTTP_GX_SECRET, so we'll need to compare that here to the correct value
+ #
+ # This is not an ideal location for this function. The reason being
+ # that because this check is done BEFORE the REMOTE_USER check, it is
+ # possible to attack the GX_SECRET key without having correct
+ # credentials. However, that's why it's not "ideal", but it is "good
+ # enough". The only users able to exploit this are ones with access to
+ # the local system (unless Galaxy is listening on 0.0.0.0....). It
+ # seems improbable that an attacker with access to the server hosting
+ # Galaxy would not have acess to Galaxy itself, and be attempting to
+ # attack the system
+ if self.config_secret_header is not None:
+ if not safe_str_cmp(environ.get('HTTP_GX_SECRET'), self.config_secret_header):
+ title = "Access to Galaxy is denied"
+ message = """
+ Galaxy is configured to authenticate users via an external
+ method (such as HTTP authentication in Apache), but an
+ incorrect shared secret key was provided by the
+ upstream (proxy) server.</p>
+ <p>Please contact your local Galaxy administrator. The
+ variable <code>remote_user_secret</code> and
+ <code>GX_SECRET</code> header must be set before you may
+ access Galaxy.
+ """
+ return self.error( start_response, title, message )
+
if not environ.get(self.remote_user_header, '(null)').startswith('(null)'):
if not environ[ self.remote_user_header ].count( '@' ):
if self.maildomain is not None:
diff -r 5e5c0cf930bb96bf8af0bd19daad1a999b40c374 -r 7a889340aba7e078d31963aa99139c7ddaea07c2 lib/galaxy/webapps/galaxy/buildapp.py
--- a/lib/galaxy/webapps/galaxy/buildapp.py
+++ b/lib/galaxy/webapps/galaxy/buildapp.py
@@ -503,8 +503,8 @@
app = RemoteUser( app, maildomain = conf.get( 'remote_user_maildomain', None ),
display_servers = util.listify( conf.get( 'display_servers', '' ) ),
admin_users = conf.get( 'admin_users', '' ).split( ',' ),
- remote_user_header = conf.get( 'remote_user_header', 'HTTP_REMOTE_USER' ) )
- log.debug( "Enabling 'remote user' middleware" )
+ remote_user_header = conf.get( 'remote_user_header', 'HTTP_REMOTE_USER' ),
+ remote_user_secret_header = conf.get('remote_user_secret', None) )
# The recursive middleware allows for including requests in other
# requests or forwarding of requests, all on the server side.
if asbool(conf.get('use_recursive', True)):
diff -r 5e5c0cf930bb96bf8af0bd19daad1a999b40c374 -r 7a889340aba7e078d31963aa99139c7ddaea07c2 lib/galaxy/webapps/tool_shed/buildapp.py
--- a/lib/galaxy/webapps/tool_shed/buildapp.py
+++ b/lib/galaxy/webapps/tool_shed/buildapp.py
@@ -157,7 +157,8 @@
from galaxy.webapps.tool_shed.framework.middleware.remoteuser import RemoteUser
app = RemoteUser( app, maildomain = conf.get( 'remote_user_maildomain', None ),
display_servers = util.listify( conf.get( 'display_servers', '' ) ),
- admin_users = conf.get( 'admin_users', '' ).split( ',' ) )
+ admin_users = conf.get( 'admin_users', '' ).split( ',' ),
+ remote_user_secret_header = conf.get('remote_user_secret', None) )
log.debug( "Enabling 'remote user' middleware" )
# The recursive middleware allows for including requests in other
# requests or forwarding of requests, all on the server side.
diff -r 5e5c0cf930bb96bf8af0bd19daad1a999b40c374 -r 7a889340aba7e078d31963aa99139c7ddaea07c2 lib/galaxy/webapps/tool_shed/config.py
--- a/lib/galaxy/webapps/tool_shed/config.py
+++ b/lib/galaxy/webapps/tool_shed/config.py
@@ -83,6 +83,7 @@
self.remote_user_maildomain = kwargs.get( "remote_user_maildomain", None )
self.remote_user_header = kwargs.get( "remote_user_header", 'HTTP_REMOTE_USER' )
self.remote_user_logout_href = kwargs.get( "remote_user_logout_href", None )
+ self.remote_user_secret = kwargs.get( "remote_user_secret", None )
self.require_login = string_as_bool( kwargs.get( "require_login", "False" ) )
self.allow_user_creation = string_as_bool( kwargs.get( "allow_user_creation", "True" ) )
self.allow_user_deletion = string_as_bool( kwargs.get( "allow_user_deletion", "False" ) )
diff -r 5e5c0cf930bb96bf8af0bd19daad1a999b40c374 -r 7a889340aba7e078d31963aa99139c7ddaea07c2 lib/galaxy/webapps/tool_shed/framework/middleware/remoteuser.py
--- a/lib/galaxy/webapps/tool_shed/framework/middleware/remoteuser.py
+++ b/lib/galaxy/webapps/tool_shed/framework/middleware/remoteuser.py
@@ -3,6 +3,7 @@
"""
import socket
+from galaxy.util import safe_str_cmp
errorpage = """
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
@@ -36,11 +37,13 @@
"""
class RemoteUser( object ):
- def __init__( self, app, maildomain=None, display_servers=None, admin_users=None ):
+ def __init__( self, app, maildomain=None, display_servers=None, admin_users=None, remote_user_secret_header=None ):
self.app = app
self.maildomain = maildomain
self.display_servers = display_servers or []
self.admin_users = admin_users or []
+ self.config_secret_header = remote_user_secret_header
+
def __call__( self, environ, start_response ):
environ[ 'webapp' ] = 'tool_shed'
# Allow display servers
@@ -53,6 +56,34 @@
if host in self.display_servers:
environ[ 'HTTP_REMOTE_USER' ] = 'remote_display_server@%s' % ( self.maildomain or 'example.org' )
return self.app( environ, start_response )
+
+ # If the secret header is enabled, we expect upstream to send along some key
+ # in HTTP_GX_SECRET, so we'll need to compare that here to the correct value
+ #
+ # This is not an ideal location for this function. The reason being
+ # that because this check is done BEFORE the REMOTE_USER check, it is
+ # possible to attack the GX_SECRET key without having correct
+ # credentials. However, that's why it's not "ideal", but it is "good
+ # enough". The only users able to exploit this are ones with access to
+ # the local system (unless Galaxy is listening on 0.0.0.0....). It
+ # seems improbable that an attacker with access to the server hosting
+ # Galaxy would not have acess to Galaxy itself, and be attempting to
+ # attack the system
+ if self.config_secret_header is not None:
+ if not safe_str_cmp(environ.get('HTTP_GX_SECRET'), self.config_secret_header):
+ title = "Access to Galaxy is denied"
+ message = """
+ Galaxy is configured to authenticate users via an external
+ method (such as HTTP authentication in Apache), but an
+ incorrect shared secret key was provided by the
+ upstream (proxy) server.</p>
+ <p>Please contact your local Galaxy administrator. The
+ variable <code>remote_user_secret</code> and
+ <code>GX_SECRET</code> header must be set before you may
+ access Galaxy.
+ """
+ return self.error( start_response, title, message )
+
# Apache sets REMOTE_USER to the string '(null)' when using the Rewrite* method for passing REMOTE_USER and a user is
# un-authenticated. Any other possible values need to go here as well.
path_info = environ.get('PATH_INFO', '')
@@ -88,6 +119,7 @@
<p>Contact your local Galaxy tool shed administrator.
"""
return self.error( start_response, title, message )
+
def error( self, start_response, title="Access denied", message="Contact your local Galaxy tool shed administrator." ):
start_response( '403 Forbidden', [('Content-type', 'text/html')] )
return [errorpage % (title, message)]
https://bitbucket.org/galaxy/galaxy-central/commits/b7deaa9fb8b7/
Changeset: b7deaa9fb8b7
User: erasche2
Date: 2014-12-18 19:43:00+00:00
Summary: Fixed typos in documentation
Affected #: 2 files
diff -r 7a889340aba7e078d31963aa99139c7ddaea07c2 -r b7deaa9fb8b7cadd141fd07b2c50654716c88720 lib/galaxy/web/framework/middleware/remoteuser.py
--- a/lib/galaxy/web/framework/middleware/remoteuser.py
+++ b/lib/galaxy/web/framework/middleware/remoteuser.py
@@ -72,7 +72,7 @@
# enough". The only users able to exploit this are ones with access to
# the local system (unless Galaxy is listening on 0.0.0.0....). It
# seems improbable that an attacker with access to the server hosting
- # Galaxy would not have acess to Galaxy itself, and be attempting to
+ # Galaxy would not have access to Galaxy itself, and be attempting to
# attack the system
if self.config_secret_header is not None:
if not safe_str_cmp(environ.get('HTTP_GX_SECRET'), self.config_secret_header):
diff -r 7a889340aba7e078d31963aa99139c7ddaea07c2 -r b7deaa9fb8b7cadd141fd07b2c50654716c88720 lib/galaxy/webapps/tool_shed/framework/middleware/remoteuser.py
--- a/lib/galaxy/webapps/tool_shed/framework/middleware/remoteuser.py
+++ b/lib/galaxy/webapps/tool_shed/framework/middleware/remoteuser.py
@@ -67,7 +67,7 @@
# enough". The only users able to exploit this are ones with access to
# the local system (unless Galaxy is listening on 0.0.0.0....). It
# seems improbable that an attacker with access to the server hosting
- # Galaxy would not have acess to Galaxy itself, and be attempting to
+ # Galaxy would not have access to Galaxy itself, and be attempting to
# attack the system
if self.config_secret_header is not None:
if not safe_str_cmp(environ.get('HTTP_GX_SECRET'), self.config_secret_header):
https://bitbucket.org/galaxy/galaxy-central/commits/ac1859e27eb0/
Changeset: ac1859e27eb0
User: jmchilton
Date: 2015-01-01 19:16:50+00:00
Summary: Merged in erasche2/galaxy-central2 (pull request #619)
Allow for shared secret between Galaxy and upstream proxies
Affected #: 9 files
diff -r 84d5a72790735ae4da03f27735c52182bb925d9b -r ac1859e27eb0217ae5dabd7495a3b9314f65a1f7 config/galaxy.ini.sample
--- a/config/galaxy.ini.sample
+++ b/config/galaxy.ini.sample
@@ -713,6 +713,13 @@
# *not* include 'HTTP_' at the beginning of the header name.
#remote_user_header = HTTP_REMOTE_USER
+# If use_remote_user is enabled, anyone who can log in to the Galaxy host may
+# impersonate any other user by simply sending the appropriate header. Thus a
+# secret shared between the upstream proxy server, and Galaxy is required.
+# If anyone other than the Galaxy user is using the server, then apache/nginx should
+# pass a value in the header 'GX_SECRET' that is identical the one below
+#remote_user_secret = USING THE DEFAULT IS NOT SECURE!
+
# If use_remote_user is enabled, you can set this to a URL that will log your
# users out.
#remote_user_logout_href = None
diff -r 84d5a72790735ae4da03f27735c52182bb925d9b -r ac1859e27eb0217ae5dabd7495a3b9314f65a1f7 config/tool_shed.ini.sample
--- a/config/tool_shed.ini.sample
+++ b/config/tool_shed.ini.sample
@@ -67,6 +67,13 @@
# https://wiki.galaxyproject.org/Admin/Config/ApacheProxy
#use_remote_user = False
+# If use_remote_user is enabled, anyone who can log in to the Galaxy host may
+# impersonate any other user by simply sending the appropriate header. Thus a
+# secret shared between the upstream proxy server, and Galaxy is required.
+# If anyone other than the Galaxy user is using the server, then apache/nginx should
+# pass a value in the header 'GX_SECRET' that is identical the one below
+#remote_user_secret = changethisinproductiontoo
+
# Configuration for debugging middleware
debug = true
use_lint = false
diff -r 84d5a72790735ae4da03f27735c52182bb925d9b -r ac1859e27eb0217ae5dabd7495a3b9314f65a1f7 lib/galaxy/config.py
--- a/lib/galaxy/config.py
+++ b/lib/galaxy/config.py
@@ -133,6 +133,7 @@
self.remote_user_maildomain = kwargs.get( "remote_user_maildomain", None )
self.remote_user_header = kwargs.get( "remote_user_header", 'HTTP_REMOTE_USER' )
self.remote_user_logout_href = kwargs.get( "remote_user_logout_href", None )
+ self.remote_user_secret = kwargs.get( "remote_user_secret", None )
self.require_login = string_as_bool( kwargs.get( "require_login", "False" ) )
self.allow_user_creation = string_as_bool( kwargs.get( "allow_user_creation", "True" ) )
self.allow_user_deletion = string_as_bool( kwargs.get( "allow_user_deletion", "False" ) )
diff -r 84d5a72790735ae4da03f27735c52182bb925d9b -r ac1859e27eb0217ae5dabd7495a3b9314f65a1f7 lib/galaxy/util/__init__.py
--- a/lib/galaxy/util/__init__.py
+++ b/lib/galaxy/util/__init__.py
@@ -1119,6 +1119,8 @@
def safe_str_cmp(a, b):
+ """safely compare two strings in a timing-attack-resistant manner
+ """
if len(a) != len(b):
return False
rv = 0
diff -r 84d5a72790735ae4da03f27735c52182bb925d9b -r ac1859e27eb0217ae5dabd7495a3b9314f65a1f7 lib/galaxy/web/framework/middleware/remoteuser.py
--- a/lib/galaxy/web/framework/middleware/remoteuser.py
+++ b/lib/galaxy/web/framework/middleware/remoteuser.py
@@ -3,6 +3,7 @@
"""
import socket
+from galaxy.util import safe_str_cmp
errorpage = """
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
@@ -37,12 +38,13 @@
class RemoteUser( object ):
- def __init__( self, app, maildomain=None, display_servers=None, admin_users=None, remote_user_header=None ):
+ def __init__( self, app, maildomain=None, display_servers=None, admin_users=None, remote_user_header=None, remote_user_secret_header=None ):
self.app = app
self.maildomain = maildomain
self.display_servers = display_servers or []
self.admin_users = admin_users or []
self.remote_user_header = remote_user_header or 'HTTP_REMOTE_USER'
+ self.config_secret_header = remote_user_secret_header
def __call__( self, environ, start_response ):
# Allow display servers
@@ -59,6 +61,34 @@
# Rewrite* method for passing REMOTE_USER and a user is
# un-authenticated. Any other possible values need to go here as well.
path_info = environ.get('PATH_INFO', '')
+
+ # If the secret header is enabled, we expect upstream to send along some key
+ # in HTTP_GX_SECRET, so we'll need to compare that here to the correct value
+ #
+ # This is not an ideal location for this function. The reason being
+ # that because this check is done BEFORE the REMOTE_USER check, it is
+ # possible to attack the GX_SECRET key without having correct
+ # credentials. However, that's why it's not "ideal", but it is "good
+ # enough". The only users able to exploit this are ones with access to
+ # the local system (unless Galaxy is listening on 0.0.0.0....). It
+ # seems improbable that an attacker with access to the server hosting
+ # Galaxy would not have access to Galaxy itself, and be attempting to
+ # attack the system
+ if self.config_secret_header is not None:
+ if not safe_str_cmp(environ.get('HTTP_GX_SECRET'), self.config_secret_header):
+ title = "Access to Galaxy is denied"
+ message = """
+ Galaxy is configured to authenticate users via an external
+ method (such as HTTP authentication in Apache), but an
+ incorrect shared secret key was provided by the
+ upstream (proxy) server.</p>
+ <p>Please contact your local Galaxy administrator. The
+ variable <code>remote_user_secret</code> and
+ <code>GX_SECRET</code> header must be set before you may
+ access Galaxy.
+ """
+ return self.error( start_response, title, message )
+
if not environ.get(self.remote_user_header, '(null)').startswith('(null)'):
if not environ[ self.remote_user_header ].count( '@' ):
if self.maildomain is not None:
diff -r 84d5a72790735ae4da03f27735c52182bb925d9b -r ac1859e27eb0217ae5dabd7495a3b9314f65a1f7 lib/galaxy/webapps/galaxy/buildapp.py
--- a/lib/galaxy/webapps/galaxy/buildapp.py
+++ b/lib/galaxy/webapps/galaxy/buildapp.py
@@ -503,8 +503,8 @@
app = RemoteUser( app, maildomain = conf.get( 'remote_user_maildomain', None ),
display_servers = util.listify( conf.get( 'display_servers', '' ) ),
admin_users = conf.get( 'admin_users', '' ).split( ',' ),
- remote_user_header = conf.get( 'remote_user_header', 'HTTP_REMOTE_USER' ) )
- log.debug( "Enabling 'remote user' middleware" )
+ remote_user_header = conf.get( 'remote_user_header', 'HTTP_REMOTE_USER' ),
+ remote_user_secret_header = conf.get('remote_user_secret', None) )
# The recursive middleware allows for including requests in other
# requests or forwarding of requests, all on the server side.
if asbool(conf.get('use_recursive', True)):
diff -r 84d5a72790735ae4da03f27735c52182bb925d9b -r ac1859e27eb0217ae5dabd7495a3b9314f65a1f7 lib/galaxy/webapps/tool_shed/buildapp.py
--- a/lib/galaxy/webapps/tool_shed/buildapp.py
+++ b/lib/galaxy/webapps/tool_shed/buildapp.py
@@ -157,7 +157,8 @@
from galaxy.webapps.tool_shed.framework.middleware.remoteuser import RemoteUser
app = RemoteUser( app, maildomain = conf.get( 'remote_user_maildomain', None ),
display_servers = util.listify( conf.get( 'display_servers', '' ) ),
- admin_users = conf.get( 'admin_users', '' ).split( ',' ) )
+ admin_users = conf.get( 'admin_users', '' ).split( ',' ),
+ remote_user_secret_header = conf.get('remote_user_secret', None) )
log.debug( "Enabling 'remote user' middleware" )
# The recursive middleware allows for including requests in other
# requests or forwarding of requests, all on the server side.
diff -r 84d5a72790735ae4da03f27735c52182bb925d9b -r ac1859e27eb0217ae5dabd7495a3b9314f65a1f7 lib/galaxy/webapps/tool_shed/config.py
--- a/lib/galaxy/webapps/tool_shed/config.py
+++ b/lib/galaxy/webapps/tool_shed/config.py
@@ -73,6 +73,7 @@
self.remote_user_maildomain = kwargs.get( "remote_user_maildomain", None )
self.remote_user_header = kwargs.get( "remote_user_header", 'HTTP_REMOTE_USER' )
self.remote_user_logout_href = kwargs.get( "remote_user_logout_href", None )
+ self.remote_user_secret = kwargs.get( "remote_user_secret", None )
self.require_login = string_as_bool( kwargs.get( "require_login", "False" ) )
self.allow_user_creation = string_as_bool( kwargs.get( "allow_user_creation", "True" ) )
self.allow_user_deletion = string_as_bool( kwargs.get( "allow_user_deletion", "False" ) )
diff -r 84d5a72790735ae4da03f27735c52182bb925d9b -r ac1859e27eb0217ae5dabd7495a3b9314f65a1f7 lib/galaxy/webapps/tool_shed/framework/middleware/remoteuser.py
--- a/lib/galaxy/webapps/tool_shed/framework/middleware/remoteuser.py
+++ b/lib/galaxy/webapps/tool_shed/framework/middleware/remoteuser.py
@@ -3,6 +3,7 @@
"""
import socket
+from galaxy.util import safe_str_cmp
errorpage = """
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
@@ -36,11 +37,13 @@
"""
class RemoteUser( object ):
- def __init__( self, app, maildomain=None, display_servers=None, admin_users=None ):
+ def __init__( self, app, maildomain=None, display_servers=None, admin_users=None, remote_user_secret_header=None ):
self.app = app
self.maildomain = maildomain
self.display_servers = display_servers or []
self.admin_users = admin_users or []
+ self.config_secret_header = remote_user_secret_header
+
def __call__( self, environ, start_response ):
environ[ 'webapp' ] = 'tool_shed'
# Allow display servers
@@ -53,6 +56,34 @@
if host in self.display_servers:
environ[ 'HTTP_REMOTE_USER' ] = 'remote_display_server@%s' % ( self.maildomain or 'example.org' )
return self.app( environ, start_response )
+
+ # If the secret header is enabled, we expect upstream to send along some key
+ # in HTTP_GX_SECRET, so we'll need to compare that here to the correct value
+ #
+ # This is not an ideal location for this function. The reason being
+ # that because this check is done BEFORE the REMOTE_USER check, it is
+ # possible to attack the GX_SECRET key without having correct
+ # credentials. However, that's why it's not "ideal", but it is "good
+ # enough". The only users able to exploit this are ones with access to
+ # the local system (unless Galaxy is listening on 0.0.0.0....). It
+ # seems improbable that an attacker with access to the server hosting
+ # Galaxy would not have access to Galaxy itself, and be attempting to
+ # attack the system
+ if self.config_secret_header is not None:
+ if not safe_str_cmp(environ.get('HTTP_GX_SECRET'), self.config_secret_header):
+ title = "Access to Galaxy is denied"
+ message = """
+ Galaxy is configured to authenticate users via an external
+ method (such as HTTP authentication in Apache), but an
+ incorrect shared secret key was provided by the
+ upstream (proxy) server.</p>
+ <p>Please contact your local Galaxy administrator. The
+ variable <code>remote_user_secret</code> and
+ <code>GX_SECRET</code> header must be set before you may
+ access Galaxy.
+ """
+ return self.error( start_response, title, message )
+
# Apache sets REMOTE_USER to the string '(null)' when using the Rewrite* method for passing REMOTE_USER and a user is
# un-authenticated. Any other possible values need to go here as well.
path_info = environ.get('PATH_INFO', '')
@@ -88,6 +119,7 @@
<p>Contact your local Galaxy tool shed administrator.
"""
return self.error( start_response, title, message )
+
def error( self, start_response, title="Access denied", message="Contact your local Galaxy tool shed administrator." ):
start_response( '403 Forbidden', [('Content-type', 'text/html')] )
return [errorpage % (title, message)]
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.