1 Before configurable failover, we had this:
8 which instructed module_authorize to first pass the request through
9 rlm_preprocess, and if that returned success, pass it through rlm_files,
10 and if that returned success, module_authorize itself would then return
11 success. Processing was strictly linear and if one module failed, the whole
12 function would fail immediately.
14 Configurable failover provides more flexibility. It takes advantage of the
15 tree structure of radiusd.conf to support a config language that allows you
16 to specify groups of modules that should work together in ways other than
17 execute-in-order-return-on-fail. Basically you can redesign the flow of
18 module_authorize to fit your needs, without touching C code, just by altering
21 I will soon explain this new language in detail, but first, if you just want
22 to know how to make failover happen without understanding why it works or how
23 to tweak it, here's your quick fix: just put a redundant{} block around those
24 module instances which refer to redundant databases. Example:
28 server="myfirstserver.example"
29 # Insert other necessary parameters here
32 server="mysecondserver.example"
33 # Insert other necessary parameters here
45 OK, now for the exhaustive documentation. The things unexpectedly CAPITALIZED
48 The fundamental object is called a MODCALLABLE, because it is something that
49 can be passed a specific radius request and returns one of the RLM_MODULE_*
50 results. It is a function - if you can accept the fact that pieces of
51 radiusd.conf are functions. There are two kinds of MODCALLABLEs: GROUPs and
54 A SINGLE is a reference to a module instance that was set up in the modules{}
55 section of radiusd.conf, like "preprocess" or "sql1". When a SINGLE is
56 called, the corresponding function in the rlm is invoked, and whichever
57 RLM_MODULE_* it returns becomes the RESULT of the SINGLE.
59 A GROUP is a section of radiusd.conf that includes some MODCALLABLEs.
60 Examples of GROUPs above include "authorize{...}", which implements the C
61 function module_authorize, and "redundant{...}", which contains two SINGLEs
62 that refer to a couple of redundant databases. Note that a GROUP can contain
63 other GROUPs - "authtype SQL{...}" is also a GROUP, which implements the C
64 function module_authenticate when Auth-Type is set to SQL.
66 Now here's the fun part - what happens when a GROUP is called? It simply runs
67 through all of its children in order, and calls each one, whether it is
68 another GROUP or a SINGLE. It then looks at the RESULT of that child, and
69 takes some ACTION, which is basically either "return that RESULT immediately"
70 or "Keep going". In the first example, any "bad" RESULT from the preprocess
71 module causes an immediate return, and any "good" RESULT causes the
72 authorize{...} GROUP to proceed to the files module.
74 We can see the exact rules by writing them out the long way:
101 This is the same as the first example, with the default behavior explicitly
102 spelled out. Each SINGLE becomes its own section, containing a list of
103 RESULTs that it may return and what ACTION should follow from them. So
104 preprocess is called, and if it returns for example RLM_MODULE_REJECT, then
105 the reject=return rule is applied, and the authorize{...} GROUP itself
106 immediately returns RLM_MODULE_REJECT.
108 If preprocess returns RLM_MODULE_NOOP, the corresponding ACTION is "2". An
109 integer ACTION serves two purposes - first, it tells the parent GROUP to go
110 on to the next module. Second, it is a hint as to how desirable this RESULT
111 is as a candidate for the GROUP's own RESULT. So files is called... suppose
112 it returns RLM_MODULE_NOTFOUND. The ACTION for notfound inside the files{...}
113 block is "1". We have now reached the end of the authorize{...} GROUP and we
114 look at the RESULTs we accumulated along the way - there is a noop with
115 preference level 2, and a notfound with preference level 1, so the
116 authorize{...} GROUP as a whole returns RLM_MODULE_NOOP, which makes sense
117 because to say the user was not found at all would be a lie, since preprocess
118 apparently found him, or else it would have returned RLM_MODULE_NOTFOUND too.
120 [Take a deep breath - the worst is over]
122 That RESULT preference/desirability stuff is pretty complex, but my hope is
123 that it will be complex enough to handle the needs of everyone's real-world
124 imperfect systems, while staying out of sight most of the time since the
125 defaults will be right for the most common configurations.
127 So where does redundant{...} fit in with all that? Well, redundant{...} is
128 simply a group that changes the default ACTIONs to something like
131 everythingelse = return
133 so that when one module fails, we keep trying until we find one that doesn't
134 fail, then return whatever it returned. And at the end, if they all failed,
135 the redundant GROUP as a whole returns RLM_MODULE_FAIL, just as you'd want it
138 There are two other kinds of grouping: group{...} which does not have any
139 specialized default ACTIONs, and append{...}, which should be used when you
140 have separate but similarly structured databases that are guaranteed not to
143 That's all that really needs to be said. But now a few random notes:
145 1. GROUPs may have RESULT=ACTION specifiers too! It would look like this:
157 which would prevent rlm_files from being called if neither of the SQL
158 instances could find the user.
160 2. redundant{...} and append{...} are just shortcuts. You could write
190 but the latter is just a whole lot easier to read.
192 3. "authenticate{...}" itself is not a GROUP, even though it contains a list
193 of authtype GROUPs, because its semantics are totally different - it uses
194 Auth-Type to decide which of its members to call, and their order is
197 4. The default rules are context-sensitive - for authorize, the defaults are
198 what you saw above - notfound, noop, ok, and updated are considered
199 success, and anything else has an ACTION of "return". For authenticate, the
200 default is to return on success *or* reject, and only try the second and
201 following items if the first one fails. You can read all the default ACTIONs
202 in modcall.c (int defaultactions[][][]), or just trust me. They do the right
205 5. There are some rules that can't be implemented in this language - things
206 like "if the absolutelypositivelymandatory module returns notfound, the group
207 should immediately return reject". It would be possible to extend the
208 language to include that, as "notfound = return-reject", and the obvious
209 followup feature would be "notfound = 1-reject", "noop = 2-ok", "ok = 3-ok",
210 etc. But I don't feel justified adding that complexity in the first draft.
211 There are already enough things here that may never see real-world usage.