This is the "configurable failover" patch, providing a more flexible
[freeradius.git] / doc / configurable_failover
1 Before configurable failover, we had this:
2
3 authorize {
4   preprocess
5   files
6 }
7
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.
13
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
19 radiusd.conf.
20
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:
25
26   modules {
27     sql sql1 {
28       server="myfirstserver.example"
29       # Insert other necessary parameters here
30     }
31     sql sql2 {
32       server="mysecondserver.example"
33       # Insert other necessary parameters here
34     }
35   }
36   authenticate {
37     authtype SQL {
38       redundant {
39         sql1
40         sql2
41       }
42     }
43   }
44
45 OK, now for the exhaustive documentation. The things unexpectedly CAPITALIZED
46 are the key terms...
47
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
52 SINGLEs.
53
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.
58
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.
65
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.
73
74 We can see the exact rules by writing them out the long way:
75
76 authorize {
77   preprocess {
78     notfound = 1
79     noop     = 2
80     ok       = 3
81     updated  = 4
82     fail     = return
83     reject   = return
84     userlock = return
85     invalid  = return
86     handled  = return
87   }
88   files {
89     notfound = 1
90     noop     = 2
91     ok       = 3
92     updated  = 4
93     fail     = return
94     reject   = return
95     userlock = return
96     invalid  = return
97     handled  = return
98   }
99 }
100
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.
107
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.
119
120 [Take a deep breath - the worst is over]
121
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.
126
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
129
130   fail = 1
131   everythingelse = return
132
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
136 to (I hope).
137
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
141 overlap.
142
143 That's all that really needs to be said. But now a few random notes:
144
145 1. GROUPs may have RESULT=ACTION specifiers too! It would look like this:
146
147   authorize {
148     preprocess
149     redundant {
150       sql1
151       sql2
152       notfound = return
153     }
154     files
155   }
156
157 which would prevent rlm_files from being called if neither of the SQL
158 instances could find the user.
159
160 2. redundant{...} and append{...} are just shortcuts. You could write
161     group {
162       sql1 {
163         fail     = 1
164         notfound = 2
165         noop     = return
166         ok       = return
167         updated  = return
168         reject   = return
169         userlock = return
170         invalid  = return
171         handled  = return
172       }
173       sql2 {
174         fail     = 1
175         notfound = 2
176         noop     = return
177         ok       = return
178         updated  = return
179         reject   = return
180         userlock = return
181         invalid  = return
182         handled  = return
183       }
184     }
185   instead of
186     redundant {
187       sql1
188       sql2
189     }
190   but the latter is just a whole lot easier to read.
191
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
195 irrelevant.
196
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
203 thing.
204
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.
212 Like append{...}
213
214 -- Pac. 9/18/2000