#8132 vault_add_internal does not work with server context
Opened 4 years ago by twoerner. Modified 4 years ago

Issue

Working on the ipavault module for ansible-freeipa revealed the issue that vault_add_internal is failing with context="server":

The full traceback is:

  File "/tmp/ansible_ipavault_payload_c9v2bouv/ansible_ipavault_payload.zip/ansible/modules/ipavault.py", line 409, in main
  File "/tmp/ansible_ipavault_payload_c9v2bouv/ansible_ipavault_payload.zip/ansible/module_utils/ansible_freeipa_module.py", line 195, in api_command
    return api.Command[command](name, **args)
  File "/usr/lib/python3.6/site-packages/ipalib/frontend.py", line 450, in __call__
    return self.__do_call(*args, **options)
  File "/usr/lib/python3.6/site-packages/ipalib/frontend.py", line 478, in __do_call
    ret = self.run(*args, **options)
  File "/usr/lib/python3.6/site-packages/ipalib/frontend.py", line 800, in run
    return self.execute(*args, **options)
  File "/usr/lib/python3.6/site-packages/ipaserver/plugins/baseldap.py", line 1190, in execute
    *keys, **options)
  File "/usr/lib/python3.6/site-packages/ipaserver/plugins/vault.py", line 788, in pre_callback
    principal = kerberos.Principal(getattr(context, 'principal'))

It seems that there is no principal defined in the server context. vault_add_internal is properly working with "cli_installer" for example.

Actual behavior

Trace back

Expected behavior

No trace back

Version/Release/Distribution

package freeipa-server is not installed
package freeipa-client is not installed
ipa-server-4.8.0-1.module+el8.1.0+3577+202f0a51.x86_64
ipa-client-4.8.0-1.module+el8.1.0+3577+202f0a51.x86_64
389-ds-base-1.4.1.3-1.module+el8.1.0+3259+a5bc8ad3.x86_64
pki-ca-10.7.1-2.module+el8.1.0+3386+52d02a00.noarch
krb5-server-1.17-7.el8.x86_64


IPA vault does not work in server context at all. Large part of the logic is implemented in ipaclient, e.g. key and data wrapping. You must use a client context.

There is a bunch of plugins that implement extra functionality on the client side. The server context should only be used by an API RPC server. Don't use the context == "server" or in_server == True for anything but the API server.

There might be an error that the command can not be used with the server context, but it should not fail this way.

The internal server error needs to be fixed.
Also, nothing really prevents vault commands to load the client side API if it needs it. 'server' context is really about running on IPA master and accessing LDAP over LDAPI. The rest should be behaving pretty much the same, only the middleware (wsgi process) is omitted.

# cat ./testrpcclient.console
from ipalib import Registry, api

register = Registry()


if 'in_server' in api.env:
    from ipalib.rpc import xmlclient, jsonclient
    register()(xmlclient)
    register()(jsonclient)

    # FIXME: api.register only looks at the class name, so we need to create
    # trivial subclasses with the desired name.
    if api.env.rpc_protocol == 'xmlrpc':

        class rpcclient(xmlclient):
            """xmlclient renamed to 'rpcclient'"""

        register()(rpcclient)

    elif api.env.rpc_protocol == 'jsonrpc':

        class rpcclient(jsonclient):
            """jsonclient renamed to 'rpcclient'"""

        register()(rpcclient)

    else:
        raise ValueError('unknown rpc_protocol: %s' % api.env.rpc_protocol)

    api.Backend.rpcclient = rpcclient(api)

if not api.Backend.rpcclient.isconnected():
    api.Backend.rpcclient.connect()

print(api.Backend.rpcclient.forward('ping'))
# ipa -e in_server=true console ./testrpcclient.console 
{'messages': ({'code': 13001, 'data': {'server_version': '2.234'}, 'message': "API Version number was not sent, forward compatibility not guaranteed. Assuming server's API version, 2.234", 'name': 'VersionMissing', 'type': 'warning'},), 'summary': 'IPA server version 4.9.0.dev201911071622+git4eba78326. API version 2.234'}

So, we can definitely make vault code more robust and actually working in the server mode. The code above is from ipaclient/plugins/rpcclient.py, a bit modified.

There are several plugins that depend on context.principal. The principal is only configured for contexts that have valid GSSAPI credentials.

And 'server' context can have valid GSSAPI credentials, as I show above.

The issue here is not lack or presence of the GSSAPI credentials. The problem is that it is rpcclient that configures them in context.principal and not something else. So if you have no rpcclient enabled, you have no context.principal while actual credentials can be in a ccache.

The rpcclient does neither set up context.principal nor use context.principal. There is exactly one place in ipa code base that sets up context.principal outside the current frame. Only the ldap2 backend configures context.principal for use by server-side plugins. The context principal is only set up for GSSAPI bound connections, not auto-bind and not for password-bind.

For password-bind, ldap2 does not configure context.principal. The same is true for LDAPI auto-bind as root.

@cheimes not sure where are you looking. ipalib/rpc.py implements RPCClient class which is exactly a backend for XML-RPC or JSON-RPC clients. It does operate on context.principal: see https://pagure.io/freeipa/blob/master/f/ipalib/rpc.py#_1003 create_connection method.

This is the code called when you are running a command (derived from ipalib.frontend.Command class) because Command.run() dispatches request to #forward() method if it is not run a server context and default implementation of #forward() performs self.Backend.rpcclient.forward() call, which, in turn, calls create_connection() if needed.

So, all we need to do is to make rpcclient available for server context and add logic in the vault code to use it explicitly where needed.

perhaps, it would be good to add a façade that performs lazy loading of the rpcclient so that we don't really load it until it is requested.

Login to comment on this ticket.

Metadata