Pluggable configuration design choices
ghudson@MIT.EDU
ghudson at MIT.EDU
Wed Jun 29 18:01:59 EDT 2011
I'm interested in feedback on the following design areas for making
krb5 configuration pluggable.
1. Module selection and registration
For system-level control of the configuration source, we need some way
of determining which configuration source to use and where the shared
object for that module can be found. Obviously, it cannot involve the
profile, so the usual method of module registration is out.
Some options are:
* A new environment variable
(e.g. KRB5_CONFIG_SOURCE=module:residual).
* Overload KRB5_CONFIG (e.g. :module:residual or
MODULE:module:residual).
* A new config file in a hardcoded location (or a new registry setting
on Windows).
* The first line of krb5.conf specifies the module and residual
somehow (perhaps using a magic comment line).
Using environment variables raises questions for how
krb5_init_secure_context() should behave.
For application-level control of the configuration source, I figure
the best choice is to bypass the plugin framework and just have the
application pass in a vtable. I'm not yet sure of the exact choice of
API (we already have three variations on krb5_init_context() and I
don't really want to make it six).
A second question is whether "module" in the above is a pathname or a
module name. If it's a module name, we would have to require module
shared objects to live in a specific place
(<libdir>/krb5/plugins/config/modname<suffix>). Or we could allow
either, depending on whether there's a slash in the name.
2. Plugin interface
Based on past input, I'm assuming that we will need read and write
methods. The core krb5 code doesn't need to write to configuration
sources, but the profile API allows applications to do so. I'll just
talk about the read methods here, and assume that the write methods
mirror them (plus a "flush" method). Of course write methods will be
optional to implement.
A configuration plugin will map keys to values. Keys will probably be
represented as a null-terminated array of C strings
(e.g. {"libdefaults", "allow-weak-crypto", NULL}). From there, some
options are:
* Minimal: Just have a single "get" method for mapping keys to a list
of strings.
* Typed: Also have methods for mapping a key to a single string, or an
integer, or a boolean value.
* Typed with fallback: if, say, get_int isn't implemented by the
plugin, call the basic get and convert the first string value to an
integer.
* Typed with two-level fallback: fallback to get_string first, and
then to the basic get method if get_string isn't implemented.
The advantage of the minimal interface is that it makes it easier to
implement pass-throughs. I am guessing people would prefer a typed
interface, though.
Do we need iteration methods? I don't think we do.
There will, of course, be a context-level initializer method which
takes a residual value,
3. KDC configuration
We have a special class of configuration consumers for programs which
run on the KDC (krb5kdc, kadmind, kdb5_util, etc.). Right now they
merge a second kdc.conf file into the profile. We'll need some
analagous behavior when a configuration plugin is in use. An obvious
choice is a boolen argument to the plugin initializer saying whether
the consumer is a KDC-type program.
4. Consumer API
Currently, applications access krb5 configuration using
krb5_get_profile() and the profile API, or the somewhat broken
krb5_appdefault_string/krb5_appdefault_boolean functions. krb5
library code accesses krb5 configuration using the profile API
directly on context->profile.
I think we can avoid changing any of that using the following design:
add a new initializer to libprofile which takes a vtable and module
data pointer instead of a list of paths. When operating on a profile
object created that way, libprofile would pass API requests through to
the vtable.
More information about the krbdev
mailing list