summaryrefslogtreecommitdiffstats
path: root/doc/antora/modules/howto/pages/protocols/dhcp/policy_device_options.adoc
blob: 05845ea8934101f0c75de02ee50204b721f992b2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
== Configure "device", "class" and "group" options

Beyond the global, network and subnet options already described, most sites
will have a number of group or class based options, and have a requirement for
setting reply parameters against individual devices.

In general, FreeRADIUS does not differentiate between "classes" (memberships
defined by some attribute of the DHCP request) and "groups" (memberships
defined by some manually aggregation related devices, typically based on lists
of MAC address).

The sample DHCP configuration provided with FreeRADIUS makes use of an internal
attribute `DHCP-Group-Name` to support the setting of different options for
different groups of devices.

In general the groups to which a device belongs is determined during the
processing of a request and these are added as instances of the
`DHCP-Group-Name` attribute. This may be by performing a test on one or more
request parameters (akin to a "class"), hash-based lookup of up all of part of
an attribute in a local list (akin to a "subclass"), or doing the same using a
remote datastore (SQL, LDAP, REST API, etc).

FreeRADIUS can then iterate over `DHCP-Group-Name` to set group-specific
options.

We describe some of these options in more detail.

=== Directly in Policy

Simple class options can be written directly into policy.  This is most
suited to those options that rarely change and are based on attributes in the
request such as the `User-Class`.

Consider the ISC DHCP configuration snippet:

[source,iscdhcp]
----
filename "undionly.kpxe";
class "pxeclient" {
    match option substring(user-class,0,4);
}
subclass "pxeclient" "iPXE" {
    filename "http://my.web.server/boot_script.php";
}
----

Or the equivalent Kea configuration:

[source,isckea]
----
"Dhcp4": {
    "option-data": [
        { "name": "boot-file-name", "data": "undionly.kpxe" }
    ],
    "client-classes": [
        {
            "name": "pxeclient",
            "test": "substring(option[77],0,4) == 'iPXE'",
            "option-data": [
               {
                   "name": "boot-file-name",
                   "data": "http://my.web.server/boot_script.php"
               }
            ]
        }
    ]
    ...
}
----

These define the "filename" DHCP option differently based on whether or not the
supplied "user-class" option begins with "iPXE".

FreeRADIUS provides multiple ways for this to be configured.

For example, the following "unlang" policy implements the class options defined
above:

[source,unlang]
----
if (&DHCP-User-Class && "%{substring:&DHCP-User-Class 0 4}" == "iPXE") {
    update reply {
        &DHCP-Boot-Filename := "http://my.web.server/boot_script.php"
    }
} else {
    update reply {
        &DHCP-Boot-Filename := "undionly.kpxe"
    }
}
----

Policy-based configuration of DHCP options is also useful for complex matching.
For example, the following Unlang sets the DHCP-Boot-Filename parameter based
on the request's DHCP-Client-Identifier using regular expression captures,
provided that it matches the given format:

[source,unlang]
----
if (&DHCP-Client-Identifier && \
        "%{string:DHCP-Client-Identifier}" =~ /^RAS([0-9])-site([A-Z])$/) {
    update reply {
        &DHCP-Boot-Filename := "rasboot-%{1}-%{2}.kpxe"
    }
}
----

=== In Text Files

The `files` module that has already been described for global, network and
subnet options can also be used to apply options to groups of clients.

Firstly we must defined a mapping from a set of clients clients to their
respective groups.  One option for this is to use the `passwd` module, for
which a sample configuration is included.

Firstly symlink or copy the module configuration
`<raddb>/mods-available/dhcp_passwd` into `<raddb>/mods-enabled/`.  The
suggested configuration expects the group membership file to be in
`<raddb>/mods-config/files/dhcp_groups` and take the form of:

[source,config]
----
<group1 name>|<hardware address>,<hardware address>,<hardware address>
<group2 name>|<hardware address>,<hardware address>
----

i.e. one line for each group starting with the group name followed by a pipe
character and then a comma-separated list of hardware addresses.

The `allow_multiple_keys` option allows for a host to be a member of
more than one group.

Sample configuration for looking up group options is contained in
`<raddb>/policy.d/dhcp` in the `dhcp_group_options` policy and in
`<raddb>/mods-available/dhcp_files` as the `dhcp_set_group_options` instance.

The same data file `<raddb>/mods-config/files/dhcp` is used to lookup
group options as was used for global and network options.  In this instance,
add entries with the group name as the key such as:

[source,config]
----
group1
       DHCP-Log-Server := 10.10.0.100,
       DHCP-LPR-Server := 10.10.0.200

group2
       DHCP-LPR-Server := 192.168.20.200
----

=== In the SQL Database

Policy and files are both read during startup and editing them while
FreeRADIUS is running will not result in any changes in behaviour.  If
you require regular changes to DHCP options, then storing them in
an SQL database provides greater flexibility since the queries will be run in
response to each DHCP packet rather than requiring the server to be restarted.

DHCP reply options for devices (including network-specific options) can be
fetched from SQL using an arbitrary lookup key. This can be performed multiple
times as necessary using different contexts, for example to first set
subnet-specific options and then to set group-specific options.

The default schema contains three tables to support this:

"dhcpreply" contains reply options for a given identifier (e.g. MAC Address):

.dhcpreply table
|===
|Identifier |Attribute |Op |Value |Context

|`02:01:aa:bb:cc:dd` |`DHCP-Log-Server` |`:=` |`192.0.2.10` |`by-mac`
|`02:01:aa:bb:cc:dd` |`DHCP-LPR-Server` |`:=` |`192.0.2.11` |`by-mac`
|`02:01:aa:bb:cc:dd` |`Fall-Through`    |`:=` |`Yes`        |`by-mac`
|===

"dhcpgroup" maps identifiers to a group of options that can be shared:

.dhcpgroup table
|===
|Identifier |GroupName |Priority |Context

|`02:01:aa:bb:cc:dd` |`salesdept` |`10` |`by-mac`
|===

"dhcpgroupreply" contains reply options for each group:

.dhcpgroupreply table
|===
|GroupName |Attribute |Op |Value |Context

|`salesdept` |`DHCP-NTP-Servers` |`:=` |`192.0.2.20` |`by-mac`
|`salesdept` |`DHCP-Log-Server`  |`+=` |`192.0.2.21` |`by-mac`
|`salesdept` |`DHCP-LPR-Server`  |`^=` |`192.0.2.22` |`by-mac`
|===

Within the context of assigning options directly to devices, as well as to
manually-curated groups of devices keyed by their MAC address:

  - Place device-specific options in the "dhcpreply" table.
  - Add `Fall-Through := Yes` to the options in the "dhcpreply" table in order
    to trigger group lookups, which are disabled by default.
  - Place entries in the "dhcpgroup" `identifier = <MAC-Address>, groupname = <group>, priority =
    <priority>` in the "dhcpgroup" table to map a device to its groups by
    priority.
  - Place the grouped options in the "dhcpgroupreply" table.
  - For each of the above, set `Context` to something by which the option
    lookup is referred to in the policy, for example `Context = 'by-mac'`.

For the above example you would add the following to the DHCP virtual server to
perform reply option lookup using the device's MAC address against the `by-mac`
context:

[source,unlang]
----
update control {
        &DHCP-SQL-Option-Context := "by-mac"
        &DHCP-SQL-Option-Identifier := &request:DHCP-Client-Hardware-Address
}
dhcp_sql.authorize
----

In the above, the DHCP reply options would be assigned to a device with MAC
address 02:01:aa:bb:cc:dd as follows:

  - Firstly, the `DHCP-Log-Server` option would be set to `192.0.2.10` and the
    `DHCP-LPR-Server` option set to `192.0.2.11`.
  - `Fall-Through` is set, so the group mapping is then queried which
    determines that the device belongs to a single `salesdept` group.
  - Finally, the options for the `salesdept` group are now merged, setting a
    `DHCP-NTP-Servers` option to `192.0.2.20`, appending an additional
    `DHCP-Log-Server` option set to `192.0.2.21`, and prepending an additional
    `DHCP-LPR-Server` option set to `192.0.2.22`.

If instead you wanted to perform a "subclass" lookup based on the first three
octets of the device's MAC address then with tables containing the following
sample data you could invoke an SQL lookup as shown:

."dhcpreply" table:
|===
|Identifier |Attribute |Op |Value |Context

|`000393` |`Fall-Through` |`:=` |`Yes` |`class-vendor`
|`000a27` |`Fall-Through` |`:=` |`Yes` |`class-vendor`
|`f40304` |`Fall-Through` |`:=` |`Yes` |`class-vendor`
|===

."dhcpgroup" table:
|===
|Identifier |GroupName |Priority |Context

|`000393` |`apple`  |`10` |`class-vendor`
|`000a27` |`apple`  |`10` |`class-vendor`
|`f40304` |`google` |`10` |`class-vendor`
|===

."dhcpgroupreply" table:
|===
|GroupName |Attribute |Op |Value |Context

|`apple`  |`DHCP-Boot-Filename` |`:=` |`apple.efi`  |`class-vendor`
|`google` |`DHCP-Boot-Filename` |`:=` |`google.efi` |`class-vendor`
|===


[source,unlang]
----
update control {
        &DHCP-SQL-Option-Context := "class-vendor"
        &DHCP-SQL-Option-Identifier := \
            "%{substring:%{hex:&DHCP-Client-Hardware-Address} 0 6}"
}
dhcp_sql.authorize
----

The file `policy.d/dhcp` contains a policy named `dhcp_policy_sql` which
provides further worked examples for different types of option lookups.

=== Testing "device", "class" and "group" options

You should now test that any device-related options that you have configured
using the various methods available are applied successfully by generating
packets containing those parameters based upon which the reply options are set.

For example, to test the iPXE user class example above you might want to
generate a request as follows:

[source,shell]
----
cat <<EOF > dhcp-packet-ipxe-boot.txt
DHCP-Message-Type := DHCP-Discover
DHCP-Client-Hardware-Address := 02:01:aa:bb:cc:dd
DHCP-User-Class := "iPXE-class-abc"
EOF
----

To which you would expect to see a response such as:

.Example output from dhcpclient
===============================
 dhcpclient: ...
 ----------------------------------------------------------------------
 Waiting for DHCP replies for: 5.000000
 ----------------------------------------------------------------------
 ...
 DHCP-Message-Type = DHCP-Offer
 DHCP-Your-IP-Address = 1.2.3.4
 DHCP-Boot-Filename := "http://my.web.server/boot_script.php"
 ...
===============================