[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [RFC][BROKEN] rbd: Allow configuration of authenticatio
From: |
Markus Armbruster |
Subject: |
Re: [Qemu-devel] [RFC][BROKEN] rbd: Allow configuration of authentication scheme |
Date: |
Fri, 20 Apr 2018 16:39:02 +0200 |
User-agent: |
Gnus/5.13 (Gnus v5.13) Emacs/25.3 (gnu/linux) |
Kevin Wolf <address@hidden> writes:
> Am 18.04.2018 um 17:06 hat Markus Armbruster geschrieben:
>> Kevin Wolf <address@hidden> writes:
>>
>> > The legacy command line syntax supports a "password-secret" option that
>> > allows to pass an authentication key to Ceph. This was not supported in
>> > QMP so far.
>>
>> We tried during development of v2.9.0 (commit 0a55679b4a5), but reverted
>> before the release:
>>
>> commit 464444fcc161284ac0e743b99251debc297d5236
>> Author: Markus Armbruster <address@hidden>
>> Date: Tue Mar 28 10:56:06 2017 +0200
>>
>> rbd: Revert -blockdev and -drive parameter auth-supported
>>
>> This reverts half of commit 0a55679. We're having second thoughts on
>> the QAPI schema (and thus the external interface), and haven't reached
>> consensus, yet. Issues include:
>>
>> * The implementation uses deprecated rados_conf_set() key
>> "auth_supported". No biggie.
>>
>> * The implementation makes -drive silently ignore invalid parameters
>> "auth" and "auth-supported.*.X" where X isn't "auth". Fixable (in
>> fact I'm going to fix similar bugs around parameter server), so
>> again no biggie.
>>
>> * BlockdevOptionsRbd member @password-secret applies only to
>> authentication method cephx. Should it be a variant member of
>> RbdAuthMethod?
>>
>> * BlockdevOptionsRbd member @user could apply to both methods cephx
>> and none, but I'm not sure it's actually used with none. If it
>> isn't, should it be a variant member of RbdAuthMethod?
>>
>> * The client offers a *set* of authentication methods, not a list.
>> Should the methods be optional members of BlockdevOptionsRbd instead
>> of members of list @auth-supported? The latter begs the question
>> what multiple entries for the same method mean. Trivial question
>> now that RbdAuthMethod contains nothing but @type, but less so when
>> RbdAuthMethod acquires other members, such the ones discussed above.
>>
>> * How BlockdevOptionsRbd member @auth-supported interacts with
>> settings from a configuration file specified with @conf is
>> undocumented. I suspect it's untested, too.
>>
>> Let's avoid painting ourselves into a corner now, and revert the
>> feature for 2.9.
>
> We tried to address all of these points in the schema of this RFC. Maybe
> things become easier if we compromise on some.
>
>> Note that users can still configure authentication methods with a
>> configuration file. They probably do that anyway if they use Ceph
>> outside QEMU as well.
>
> This solution that we originally intended to offer was dismissed by
> libvirt as unpractical: libvirt allows the user to specify both a config
> file and a key, and if it wanted to use a config file to pass the key,
> it would have to create a merged config file and keep it sync with the
> user config file at all times. Understandable that they want to avoid
> this.
>
>> Further note that this doesn't affect use of key "auth-supported" in
>> -drive file=rbd:...:key=value.
>>
>> qemu_rbd_array_opts()'s parameter @type now must be RBD_MON_HOST,
>> which is silly. This will be cleaned up shortly.
>>
>> Signed-off-by: Markus Armbruster <address@hidden>
>> Reviewed-by: Max Reitz <address@hidden>
>> Reviewed-by: Eric Blake <address@hidden>
>> Reviewed-by: Jeff Cody <address@hidden>
>> Message-id: address@hidden
>> Signed-off-by: Jeff Cody <address@hidden>
>>
>> > This patch introduces authentication options in the QAPI schema, makes
>> > them do the corresponding rados_conf_set() calls and adds compatibility
>> > code that translates the old "password-secret" option both for opening
>>
>> Suggest 'the old "password-secret" command line option parameter'.
>>
>> > and creating images to the new set of options.
>> >
>> > Note that the old option didn't allow to explicitly specify the set of
>>
>> Likewise, 'the old option parameter'.
>>
>> > allowed authentication schemes. The compatibility code assumes that if
>> > "password-secret" is given, only the cephx scheme is allowed. If it's
>> > missing, both none and cephx are allowed because the configuration file
>> > could still provide a key.
>>
>> This is a bit terse for readers who aren't familiar with the way things
>> work now (or have since forgotten pretty much everything about it, like
>> me).
>>
>> Perhaps spelling out how the compatibility code translates the old
>> option parameter to BlockdevOptionsRbd would help.
>>
>> > Signed-off-by: Kevin Wolf <address@hidden>
>> > ---
>> >
>> > This doesn't actually work correctly yet because the way that options
>> > are passed through the block layer (QAPI -> QemuOpts -> QDict). Before
>> > we fix or hack around this, let's make sure this is the schema that we
>> > want.
>>
>> Makes sense.
>>
>> > The two known problems are:
>> >
>> > 1. QDict *options in qemu_rbd_open() may contain options either in their
>> > proper QObject types (if passed from blockdev-add) or as strings
>> > (-drive). Both forms can be mixed in the same options QDict.
>>
>> Remind me, how can such a mix happen?
>
> The way I'm aware of is that you use blockdev-add, so you get the real
> types for some options, and then the block layer adds strings for
> default values.
I see.
>> > rbd uses the keyval visitor to convert the options into a QAPI
>> > object. This means that it requires all options to be strings. This
>> > patch, however, introduces a bool property, so the visitor breaks
>> > when it gets its input from blockdev-add.
>> >
>> > Options to hack around the problem:
>> >
>> > a. Do an extra conversion step or two and go through QemuOpts like
>> > some other block drivers. When I offered something like this to
>> > Markus a while ago in a similar case, he rejected the idea.
>>
>> Going through QemuOpts just to get rid of strings is a bit like going
>> down Niagara Falls in a barrel just to get rid of sleepiness: it'll do
>> that, sure, but it's probably not all it'll do.
>
> Nice comparison. I tend to agree, though there is much more precedence
> for this one than for all other options. Almost all block drivers use
> QemuOpts to parse their driver-specific options from the QDict.
For historical reasons. If I remember correctly, QemuOpts got there
first, QDict was spliced in later.
>> > b. Introduce a qdict_stringify_entries() that just does what its name
>> > says. It would be called before the running keyval visitor so that
>> > only strings will be present in the QDict.
>>
>> Precedence: a bunch of other QDict operations that are used only by the
>> block layer. One more won't make a difference, I guess.
>>
>> Aside: I'm tempted to move them out of qdict.h to reduce the temptation
>> to use them outside the block layer.
>
> Unrelated cleanup, but seems reasonable. block/qdict.h?
Works for me. No rush.
>> > c. Do a local one-off hack that checks if the entry with the key
>> > "auth-none" is a QBool, and if so, overwrite it with a string. The
>> > problem will reappear with the next non-string option.
>>
>> Defensible only if we're fairly confident such options will remain rare.
>>
>> > (d. Get rid of the QDict detour and work only with QAPI objects
>> > everywhere. Support rbd authentication only in QEMU 4.0.)
>>
>> This is of course the proper solution, but it's a big project that will
>> take time. The occasional stop gaps are unavoidable. We just need to
>> take care not to spend all of our cycles on stop gaps, and none on
>> actual progress.
>>
>> e. Make the keyval visitor accept scalars other than strings.
>>
>> More efficient than b., doubt it matters. Complicates the visitor.
>> Harder to back out than b. when we no longer need it.
>>
>> I'm leaning towards b.
>
> Okay, that's what I was leaning towards, too.
Good.
>> > 2. The proposed schema allows 'auth-cephx': {} as a valid option with
>> > the meaning that the cephx authentication scheme is enabled, but no
>> > key is given (e.g. it is taken from the config file).
>>
>> Apropos config file: we need to be careful to maintain the QAPI schema's
>> promise that "Values in the configuration file will be overridden by
>> options specified via QAPI."
>
> Yes, though it's made a bit easier by the fact that most options are
> implemented with a rados_conf_set() call. If you call the function, you
> override the config, if not, you don't.
Good.
As mentioned in my reply to Dan, the config file can't serve as stable
interface here: we break it whenever we add options to QAPI. Can't see
anything we could do but document the issue.
>> > However, an empty dict cannot currently be represented by flattened
>> > QDicts.
>>
>> Flattening a QDict moves nested scalars to the root (with dotted keys),
>> and drops nested non-scalars, i.e. QDict, QList. A nested empty QDict
>> (or QList) vanishes without trace.
>>
>> > We need to find a way to enable this. I think this will be
>> > externally visible because it directly translates into the dotted
>> > syntax of -blockdev, so we may want to be careful.
>>
>> Quote keyval.c:
>>
>> * Design flaw: there is no way to denote an empty array or non-root
>> * object. While interpreting "key absent" as empty seems natural
>> * (removing a key-val from the input string removes the member when
>> * there are more, so why not when it's the last), it doesn't work:
>> * "key absent" already means "optional object/array absent", which
>> * isn't the same as "empty object/array present".
>>
>> Getting that syntax right was hairy (you'll probably remember the
>> lengthy e-mail discussions). I'm reluctant to revisit it. We concluded
>> back then that dotted key syntax capable of expressing arbitrary JSON
>> wasn't in the cards, but that the cases it can't do are so exotic that
>> users should just fall back to JSON. And that would be my advice here.
>>
>> Can we design a schema that avoids this case? Let's see below.
>
> Can we design such a schema? Yes.
>
> Will we like it enough to accept it as a compromise? Maybe.
That's fine. We explore the design space, and pick what we hate least.
This RFC is a fine start.
>> > Any thoughts on the proposed QAPI schema or the two implementation
>> > problems are welcome.
>> >
>> > qapi/block-core.json | 22 +++++++++++
>> > block/rbd.c | 102
>> > ++++++++++++++++++++++++++++++++++++++-------------
>> > 2 files changed, 99 insertions(+), 25 deletions(-)
>> >
>> > diff --git a/qapi/block-core.json b/qapi/block-core.json
>> > index c50517bff3..d5ce588add 100644
>> > --- a/qapi/block-core.json
>> > +++ b/qapi/block-core.json
>> > @@ -3170,6 +3170,19 @@
>> >
>> >
>> > ##
>> > +# @RbdAuthCephx:
>> > +#
>> > +# @key-secret: ID of a QCryptoSecret object providing a key for
>> > cephx
>> > +# authentication. If not specified, a key from the
>> > +# specified configuration file, or the system default
>> > +# configuration is used, if present.
>> > +#
>> > +# Since: 2.13
>> > +##
>> > +{ 'struct': 'RbdAuthCephx',
>> > + 'data': { '*key-secret': 'str' } }
>> > +
>> > +##
>> > # @BlockdevOptionsRbd:
>> > #
>> > # @pool: Ceph pool name.
>> > @@ -3184,6 +3197,13 @@
>> > #
>> > # @user: Ceph id name.
>> > #
>> > +# @auth-none: true if connecting to a server without
>> > authentication
>> > +# should be allowed (default: false; since 2.13)
>> > +#
>> > +# @auth-cephx: Configuration for cephx authentication if
>> > specified. If
>> > +# not specified, cephx authentication is not allowed.
>> > +# (since 2.13)
>> > +#
>> > # @server: Monitor host address and port. This maps
>> > # to the "mon_host" Ceph option.
>> > #
>> > @@ -3195,6 +3215,8 @@
>> > '*conf': 'str',
>> > '*snapshot': 'str',
>> > '*user': 'str',
>> > + '*auth-none': 'bool',
>> > + '*auth-cephx': 'RbdAuthCephx',
>> > '*server': ['InetSocketAddressBase'] } }
>> >
>> > ##
>>
>> Two new optional members @auth-none, @auth-cephx.
>>
>> For backwards compatibility, behavior needs to remain unchanged when
>> both are absent. What is the behavior we need to preserve? Config
>> file, then default?
>
> Yes. (rados_conf_set("auth_client_required") is not called at all.)
>
>> The schema permits four cases, which get translated to an auth client
>> required setting by qemu_rbd_set_auth(), visible below:
>>
>> (1) just auth-none: we set auth_client_required to "none"
>>
>> (2) just auth-cephx: we set auth_client_required to "cephx"
>>
>> (3) both: we set auth_client_required to "none;cephx", which I can't
>> find in the documentation right now, but according to my notes means
>> "either method"
>
> The state of the Ceph documentation indeed wasn't very helpful. On the
> other hand, I'm afraid we're not in a position to complain about bad
> documentation...
Right on both counts.
>> (4) neither: rejected with "No authentication scheme allowed"
>> Uh, why isn't this a compatibility break? Or am I confused?
>
> It is, and that is one of the reasons why this patch is broken. I failed
> to mention this in the commit message, but I replied to the patch the
> next day to add this.
You mean I'm supposed to read the whole thread before I start posting to
it?!?
>> When auth-cephx is present, we get subcases
>>
>> (2a) and (3a) key-secret present: key is in the QCryptoSecret named, and
>> we set
>>
>> (2b) and (3b) key-secret absent: key must be provided some other way,
>> say in a configuration file, default keyring, whatever, or else
>> we'll fail somehow, I guess
>>
>> Related: existing BlockdevOptionsRbd member @user. As far as I know,
>> it's meaningless with auth-none.
>
> I don't know otherwise and suspect you are right, but Jeff and I
> couldn't find any definite confirmation for this assumption, so we left
> it where it is instead of moving it into RbdAuthCephx.
I pestered Jason Dillaman about this stuff back then, and he explained
If the auth is set to none, the user is technically irreverent. I
can run a command like "rbd --id this_user_does_not_exist rm
image_name" and it will succeed if cephx is disabled on the server.
Off list, Jeff was cc'ed, you weren't.
>> Ways to avoid the troublesome auth-cephx: {}:
>>
>> (A) Make auth-cephx a bool, rely on the other ways to provide the key
>>
>> (B) Make auth-cephx a bool and add the @key-secret member next to it
>>
>> Like @user, the member is meaningless with auth-none.
>>
>> (C) Make auth-cephx.key-secret a mandatory StrOrNull
>>
>> Should take care of "vanishes on flattening" problem, but dotted key
>> syntax is still screwed: since any value is a valid string, there's
>> none left for null. My take would be that if you want to say
>> auth-cephx.key-secret: {}, you get to say it in JSON.
>>
>> Aside: check_alternate() tries to catch such alternates, but we
>> failed to update it when we added first class null. *sigh*
>>
>> Which one do you hate least?
>
> What should happen with null when you stringify it? If we want to take
> the options QDict, stringify all entries and run them through the keyval
> visitor, I'm almost sure that null will be lost.
For the stringify idea to work, the keyval visitor needs to map the
string right back to the original value.
The keyval visitor currently requires the value to be null, not a
string.
Therefore, the stringify operation must leave null alone. Not pretty,
but works.
You might ask why not change the keyval visitor instead so it expects ""
rather than null. That's no good, because it makes StrOrNull ambiguous.
keyval.c can only create string scalars. I think "use JSON if you want
to specify null" is still good enough. We can make keyval.c more
expressive if we need to, but (1) I don't think we should block this
work on it, and (2) see "hairy" above.
> So (A) doesn't work unless we can specify what "other ways" are that are
> acceptable for libvirt,
Yes.
> and (C) probably doesn't play well with b. above
> (the qdict_stringify_entries() for handling mixed type QDicts).
I think it could be done, and tried to sketch how.
> Looks like only (B) is left as viable, so that's automatically the one I
> hate least of these. If we can fix the problems, I think I'd prefer (C),
> though.
I could accept (B), in particular since it mirrors existing @user.
I'm happy to help with exploring (C).
What's the next step?
- Re: [Qemu-devel] [RFC][BROKEN] rbd: Allow configuration of authentication scheme, (continued)
- Re: [Qemu-devel] [RFC][BROKEN] rbd: Allow configuration of authentication scheme, Markus Armbruster, 2018/04/18
- Re: [Qemu-devel] [RFC][BROKEN] rbd: Allow configuration of authentication scheme, Kevin Wolf, 2018/04/18
- Re: [Qemu-devel] [RFC][BROKEN] rbd: Allow configuration of authentication scheme, Daniel P . Berrangé, 2018/04/18
- Re: [Qemu-devel] [RFC][BROKEN] rbd: Allow configuration of authentication scheme, Kevin Wolf, 2018/04/18
- Re: [Qemu-devel] [RFC][BROKEN] rbd: Allow configuration of authentication scheme, Daniel P . Berrangé, 2018/04/18
- Re: [Qemu-devel] [RFC][BROKEN] rbd: Allow configuration of authentication scheme, Markus Armbruster, 2018/04/20
- Re: [Qemu-devel] [RFC][BROKEN] rbd: Allow configuration of authentication scheme, Daniel P . Berrangé, 2018/04/20
- Re: [Qemu-devel] [RFC][BROKEN] rbd: Allow configuration of authentication scheme, Markus Armbruster, 2018/04/20
- Re: [Qemu-devel] [RFC][BROKEN] rbd: Allow configuration of authentication scheme, Daniel P . Berrangé, 2018/04/20
- Re: [Qemu-devel] [RFC][BROKEN] rbd: Allow configuration of authentication scheme, Markus Armbruster, 2018/04/20
- Re: [Qemu-devel] [RFC][BROKEN] rbd: Allow configuration of authentication scheme,
Markus Armbruster <=
- Re: [Qemu-devel] [RFC][BROKEN] rbd: Allow configuration of authentication scheme, Jeff Cody, 2018/04/24
- Re: [Qemu-devel] [RFC][BROKEN] rbd: Allow configuration of authentication scheme, Kevin Wolf, 2018/04/25
- Re: [Qemu-devel] [RFC][BROKEN] rbd: Allow configuration of authentication scheme, Jeff Cody, 2018/04/27