Most RAPI resources do not require authentication for the “GET” method.
In some setups it can be desirable to always require authentication.
This patch adds a command line parameter to always require it.
Some unrelated minor typos in the “ganeti-rapi” man page are also fixed.
Signed-off-by: Michael Hanselmann <hansmi@google.com>
Reviewed-by: Guido Trotter <ultrotter@google.com>
====
+Version 2.8.0 beta1
+-------------------
+
+*(unreleased)*
+
+- The :doc:`Remote API <rapi>` daemon now supports a command line flag
+ to always require authentication, ``--require-authentication``. It can
+ be specified in ``$sysconfdir/default/ganeti``.
+
+
Version 2.7.0 beta1
-------------------
"""Mocking out the RAPI server parts.
"""
- def __init__(self, user_fn, luxi_client):
+ def __init__(self, user_fn, luxi_client, reqauth=False):
"""Initialize this class.
@type user_fn: callable
"""
self.handler = \
- server.rapi.RemoteApiHandler(user_fn, _client_cls=luxi_client)
+ server.rapi.RemoteApiHandler(user_fn, reqauth, _client_cls=luxi_client)
def FetchResponse(self, path, method, headers, request_body):
"""This is a callback method used to fetch a response.
"""
AUTH_REALM = "Ganeti Remote API"
- def __init__(self, user_fn, _client_cls=None):
+ def __init__(self, user_fn, reqauth, _client_cls=None):
"""Initializes this class.
@type user_fn: callable
@param user_fn: Function receiving username as string and returning
L{http.auth.PasswordFileUser} or C{None} if user is not found
+ @type reqauth: bool
+ @param reqauth: Whether to require authentication
"""
# pylint: disable=W0233
self._client_cls = _client_cls
self._resmap = connector.Mapper()
self._user_fn = user_fn
+ self._reqauth = reqauth
@staticmethod
def FormatErrorMessage(values):
"""Determine whether authentication is required.
"""
- return bool(self._GetRequestContext(req).handler_access)
+ return self._reqauth or bool(self._GetRequestContext(req).handler_access)
def Authenticate(self, req, username, password):
"""Checks whether a user can access a resource.
users = RapiUsers()
- handler = RemoteApiHandler(users.Get)
+ handler = RemoteApiHandler(users.Get, options.reqauth)
# Setup file watcher (it'll be driven by asyncore)
SetupFileWatcher(pathutils.RAPI_USERS_FILE,
usage="%prog [-f] [-d] [-p port] [-b ADDRESS]",
version="%%prog (ganeti) %s" %
constants.RELEASE_VERSION)
+ parser.add_option("--require-authentication", dest="reqauth",
+ default=False, action="store_true",
+ help=("Disable anonymous HTTP requests and require"
+ " authentication"))
daemon.GenericMain(constants.RAPI, parser, CheckRapi, PrepRapi, ExecRapi,
default_ssl_cert=pathutils.RAPI_CERT_FILE,
Synopsis
--------
-**ganeti-rapi** [-d] [-f] [\--no-ssl] [-K *SSL_KEY_FILE*] [-C
-*SSL_CERT_FILE*]
+| **ganeti-rapi** [-d] [-f] [\--no-ssl] [-K *SSL_KEY_FILE*]
+| [-C *SSL_CERT_FILE*] [\--require-authentication]
DESCRIPTION
-----------
``--no-ssl`` option, or alternatively the certificate used can be
changed via the ``-C`` option and the key via the ``-K`` option.
-The daemon will listen to the "ganeti-rapi" tcp port, as listed in the
+The daemon will listen to the "ganeti-rapi" TCP port, as listed in the
system services database, or if not defined, to port 5080 by default.
See the *Ganeti remote API* documentation for further information.
Most query operations are allowed without authentication. Only the
modification operations require authentication, in the form of basic
-authentication.
+authentication. Specify the ``--require-authentication`` command line
+flag to always require authentication.
The users and their rights are defined in the
-``@LOCALSTATEDIR@/lib/ganeti/rapi/users`` file. Format of this file is
-described in the Ganeti documentation (``rapi.html``).
+``@LOCALSTATEDIR@/lib/ganeti/rapi/users`` file. The format of this file
+is described in the Ganeti documentation (``rapi.html``).
.. vim: set textwidth=72 :
.. Local Variables:
return None
def _Test(self, method, path, headers, reqbody,
- user_fn=NotImplemented, luxi_client=NotImplemented):
- rm = rapi.testutils._RapiMock(user_fn, luxi_client)
+ user_fn=NotImplemented, luxi_client=NotImplemented,
+ reqauth=False):
+ rm = rapi.testutils._RapiMock(user_fn, luxi_client, reqauth=reqauth)
(resp_code, resp_headers, resp_body) = \
rm.FetchResponse(path, method, http.ParseHeaders(StringIO(headers)),
self.assertEqual(code, http.HTTP_OK)
self.assertTrue(data is None)
+ def testRootReqAuth(self):
+ (code, _, _) = self._Test(http.HTTP_GET, "/", "", None, reqauth=True)
+ self.assertEqual(code, http.HttpUnauthorized.code)
+
def testVersion(self):
(code, _, data) = self._Test(http.HTTP_GET, "/version", "", None)
self.assertEqual(code, http.HTTP_OK)
path = "/2/instances/inst1.example.com/console"
for method in rapi.baserlib._SUPPORTED_METHODS:
- # No authorization
- (code, _, _) = self._Test(method, path, "", "")
+ for reqauth in [False, True]:
+ # No authorization
+ (code, _, _) = self._Test(method, path, "", "", reqauth=reqauth)
- if method == http.HTTP_GET:
- self.assertEqual(code, http.HttpUnauthorized.code)
- else:
- self.assertEqual(code, http.HttpNotImplemented.code)
+ if method == http.HTTP_GET or reqauth:
+ self.assertEqual(code, http.HttpUnauthorized.code)
+ else:
+ self.assertEqual(code, http.HttpNotImplemented.code)
class _FakeLuxiClientForQuery: