Added rlm_always documentation, with examples
[freeradius.git] / doc / configurable_failover
1         Configurable Fail Over
2
3 Before configurable failover, we had this:
4
5 authorize {
6   preprocess
7   files
8 }
9
10 which instructed module_authorize to first pass the request through
11 rlm_preprocess, and if that returned success, pass it through rlm_files,
12 and if that returned success, module_authorize itself would then return
13 success. Processing was strictly linear and if one module failed, the whole
14 function would fail immediately.
15
16 Configurable failover provides more flexibility. It takes advantage of the
17 tree structure of radiusd.conf to support a config language that allows you
18 to specify groups of modules that should work together in ways other than
19 execute-in-order-return-on-fail. Basically you can redesign the flow of
20 module_authorize to fit your needs, without touching C code, just by altering
21 radiusd.conf.
22
23 I will soon explain this new language in detail, but first, if you just want
24 to know how to make failover happen without understanding why it works or how
25 to tweak it, here's your quick fix: just put a redundant{} block around those
26 module instances which refer to redundant databases. Example:
27
28   modules {
29     sql sql1 {
30       server="myfirstserver.example"
31       # Insert other necessary parameters here
32     }
33     sql sql2 {
34       server="mysecondserver.example"
35       # Insert other necessary parameters here
36     }
37     always handled {
38       rcode = handled
39     }
40   }
41   authenticate {
42     authtype SQL {
43       redundant {
44         sql1                    # try module sql1
45         sql2                    # if that's down, try module sql2
46         handled                 # otherwise drop the request as
47                                 # it's been "handled" by the "always"
48                                 # module (see doc/rlm_always)
49       }
50     }
51   }
52
53 OK, now for the exhaustive documentation. The things unexpectedly CAPITALIZED
54 are the key terms...
55
56 The fundamental object is called a MODCALLABLE, because it is something that
57 can be passed a specific radius request and returns one of the RLM_MODULE_*
58 results. It is a function - if you can accept the fact that pieces of
59 radiusd.conf are functions. There are two kinds of MODCALLABLEs: GROUPs and
60 SINGLEs.
61
62 A SINGLE is a reference to a module instance that was set up in the modules{}
63 section of radiusd.conf, like "preprocess" or "sql1". When a SINGLE is
64 called, the corresponding function in the rlm is invoked, and whichever
65 RLM_MODULE_* it returns becomes the RESULT of the SINGLE.
66
67 A GROUP is a section of radiusd.conf that includes some MODCALLABLEs.
68 Examples of GROUPs above include "authorize{...}", which implements the C
69 function module_authorize, and "redundant{...}", which contains two SINGLEs
70 that refer to a couple of redundant databases. Note that a GROUP can contain
71 other GROUPs - "authtype SQL{...}" is also a GROUP, which implements the C
72 function module_authenticate when Auth-Type is set to SQL.
73
74 Now here's the fun part - what happens when a GROUP is called? It simply runs
75 through all of its children in order, and calls each one, whether it is
76 another GROUP or a SINGLE. It then looks at the RESULT of that child, and
77 takes some ACTION, which is basically either "return that RESULT immediately"
78 or "Keep going". In the first example, any "bad" RESULT from the preprocess
79 module causes an immediate return, and any "good" RESULT causes the
80 authorize{...} GROUP to proceed to the files module.
81
82 We can see the exact rules by writing them out the long way:
83
84 authorize {
85   preprocess {
86     notfound = 1
87     noop     = 2
88     ok       = 3
89     updated  = 4
90     fail     = return
91     reject   = return
92     userlock = return
93     invalid  = return
94     handled  = return
95   }
96   files {
97     notfound = 1
98     noop     = 2
99     ok       = 3
100     updated  = 4
101     fail     = return
102     reject   = return
103     userlock = return
104     invalid  = return
105     handled  = return
106   }
107 }
108
109 This is the same as the first example, with the default behavior explicitly
110 spelled out. Each SINGLE becomes its own section, containing a list of
111 RESULTs that it may return and what ACTION should follow from them. So
112 preprocess is called, and if it returns for example RLM_MODULE_REJECT, then
113 the reject=return rule is applied, and the authorize{...} GROUP itself
114 immediately returns RLM_MODULE_REJECT.
115
116 If preprocess returns RLM_MODULE_NOOP, the corresponding ACTION is "2". An
117 integer ACTION serves two purposes - first, it tells the parent GROUP to go
118 on to the next module. Second, it is a hint as to how desirable this RESULT
119 is as a candidate for the GROUP's own RESULT. So files is called... suppose
120 it returns RLM_MODULE_NOTFOUND. The ACTION for notfound inside the files{...}
121 block is "1". We have now reached the end of the authorize{...} GROUP and we
122 look at the RESULTs we accumulated along the way - there is a noop with
123 preference level 2, and a notfound with preference level 1, so the
124 authorize{...} GROUP as a whole returns RLM_MODULE_NOOP, which makes sense
125 because to say the user was not found at all would be a lie, since preprocess
126 apparently found him, or else it would have returned RLM_MODULE_NOTFOUND too.
127
128 [Take a deep breath - the worst is over]
129
130 That RESULT preference/desirability stuff is pretty complex, but my hope is
131 that it will be complex enough to handle the needs of everyone's real-world
132 imperfect systems, while staying out of sight most of the time since the
133 defaults will be right for the most common configurations.
134
135 So where does redundant{...} fit in with all that? Well, redundant{...} is
136 simply a group that changes the default ACTIONs to something like
137
138   fail = 1
139   everythingelse = return
140
141 so that when one module fails, we keep trying until we find one that doesn't
142 fail, then return whatever it returned. And at the end, if they all failed,
143 the redundant GROUP as a whole returns RLM_MODULE_FAIL, just as you'd want it
144 to (I hope).
145
146 There are two other kinds of grouping: group{...} which does not have any
147 specialized default ACTIONs, and append{...}, which should be used when you
148 have separate but similarly structured databases that are guaranteed not to
149 overlap.
150
151 That's all that really needs to be said. But now a few random notes:
152
153 1. GROUPs may have RESULT=ACTION specifiers too! It would look like this:
154
155   authorize {
156     preprocess
157     redundant {
158       sql1
159       sql2
160       notfound = return
161     }
162     files
163   }
164
165 which would prevent rlm_files from being called if neither of the SQL
166 instances could find the user.
167
168 2. redundant{...} and append{...} are just shortcuts. You could write
169     group {
170       sql1 {
171         fail     = 1
172         notfound = 2
173         noop     = return
174         ok       = return
175         updated  = return
176         reject   = return
177         userlock = return
178         invalid  = return
179         handled  = return
180       }
181       sql2 {
182         fail     = 1
183         notfound = 2
184         noop     = return
185         ok       = return
186         updated  = return
187         reject   = return
188         userlock = return
189         invalid  = return
190         handled  = return
191       }
192     }
193   instead of
194     redundant {
195       sql1
196       sql2
197     }
198   but the latter is just a whole lot easier to read.
199
200 3. "authenticate{...}" itself is not a GROUP, even though it contains a list
201 of authtype GROUPs, because its semantics are totally different - it uses
202 Auth-Type to decide which of its members to call, and their order is
203 irrelevant.
204
205 4. The default rules are context-sensitive - for authorize, the defaults are
206 what you saw above - notfound, noop, ok, and updated are considered
207 success, and anything else has an ACTION of "return". For authenticate, the
208 default is to return on success *or* reject, and only try the second and
209 following items if the first one fails. You can read all the default ACTIONs
210 in modcall.c (int defaultactions[][][]), or just trust me. They do the right
211 thing.
212
213 5. There are some rules that can't be implemented in this language - things
214 like "if the absolutelypositivelymandatory module returns notfound, the group
215 should immediately return reject". It would be possible to extend the
216 language to include that, as "notfound = return-reject", and the obvious
217 followup feature would be "notfound = 1-reject", "noop = 2-ok", "ok = 3-ok",
218 etc. But I don't feel justified adding that complexity in the first draft.
219 There are already enough things here that may never see real-world usage.
220 Like append{...}
221
222 -- Pac. 9/18/2000