summaryrefslogtreecommitdiffstats
path: root/doc/nis-getting-started.txt
blob: e150beb6ef7c3a92d0a44d27a238e61c56f9f51d (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
= Getting Started with the NIS Server Plugin =

As an example, let's set up a NIS server to serve user and group
information for a domain named "example.com".

Because NIS servers need to listen on privileged ports, and because the
directory server is usually configured to drop privileges at startup,
getting the plugin running is usually a two-step process.  First, the
server must be configured to load the plugin, and then the server must
be restarted to actually get the plugin loaded.

== Loading the plugin ==

While the server is running (or not), add an entry like this to the
cn=config tree:

    dn: cn=NIS Server, cn=plugins, cn=config
    objectClass: top
    objectClass: nsSlapdPlugin
    objectClass: extensibleObject
    cn: NIS Server
    nsslapd-pluginPath: /usr/lib/dirsrv/plugins/nisserver-plugin.so
    nsslapd-pluginInitfunc: nis_plugin_init
    nsslapd-pluginType: postoperation
    nsslapd-pluginEnabled: on
    nsslapd-pluginDescription: NIS Server Plugin
    nsslapd-pluginVendor: redhat.com
    nsslapd-pluginVersion: 0
    nsslapd-pluginID: nis-plugin
    nis-tcp-wrappers-name: ypserv
    nsslapd-pluginarg0: 541

The name of the entry can be anything you like, but the path to the
plugin itself needs to be correct for your system.

In this example, we set the options that are required for loading a
plugin (the path, init function, the type of object, and so on) and two
options which are specific to the module -- the name used when
evaluating the tcp_wrappers (/etc/hosts.allow and /etc/hosts.deny)
configuration, and the port number on which we want the server to
listen.  We could leave the port number unset and let the server select
a port at startup-time, but my example system is firewalled, and I need
to know which port it'll be using in order to punch a hole in the
firewall for it.

If the entry was added to a running server, then the server should be
restarted.  Running the "/usr/sbin/rpcinfo -p | grep ypserv" command
should now show that the server is running and listening on port 541:

    # /usr/sbin/rpcinfo -p | grep ypserv
    100004    2   udp    541  ypserv
    100004    2   tcp    541  ypserv

The module itself isn't configured to serve any maps for any domains
yet, but we're doing pretty well so far.

== Configuring a Domain and a Map ==

Before we can start serving any data using the NIS protocols, we need to
have some idea of what the data in the directory looks like.  For this
example, we'll assume that the directory's main naming suffix is
"dc=example, dc=com", that under it there are separate containers for
users and groups with relative names "ou=People" and "ou=Groups",
respectively, and that we have two users ("timtom" and "kevin") who both
belong to two groups ("minions" and "moppets").  The tree looks
something like this:

   + dc=example,dc=com
     + ou=People,dc=example,dc=com
       + uid=timtom,ou=People,dc=example,dc=com
       + uid=kevin,ou=People,dc=example,dc=com
     + ou=Groups,dc=example,dc=com
       + cn=minions,ou=Groups,dc=example,dc=com
       + cn=moppets,ou=Groups,dc=example,dc=com

In order to expose data from directory server entries via a NIS map, the
plugin needs to be provided with enough information to let it find the
set of directory entries which need to be exposed, and it needs to be
provided with instructions for building the key/value pairs which
constitute the map by using the contents of those entries.  Of course,
it also needs to know the name of the NIS domain and the map's name.

The plugin expects that information to be provided in an entry which is
an immediate child of the entry which caused the plugin to be loaded.
To avoid having to make up a more meaningful name, we'll just use the
combination of the map name and the domain name to name the map's
configuration entry.

    dn: nis-domain=example.com+nis-map=users,cn=NIS Server,cn=plugins,cn=config
    objectclass: extensibleObject
    nis-domain: example.com
    nis-map: users
    nis-base: ou=People, dc=example, dc=com
    nis-filter: (objectClass=posixAccount)
    nis-key-format: %{uid}
    nis-value-format: %{uid}

In this example, we've defined a map named "users" in a domain named
"example.com".  The entries which will be used to construct the map's
contents will be found by performing a subtree search starting at
"ou=People,dc=example,dc=com" for entries matching the filter
"(objectClass=posixAccount").  For each entry that is found by the
search, the "uid" attribute will be used as the key in the NIS map, and
the corresponding value will also be the value of the "uid" attribute.

And just like that, our NIS server is serving the domain, and the
"ypbind" service running on a client system will be able to find it.  Of
course, that domain contains only one not-very-useful map, but that's a
different problem.

== Formatting Entries ==

So far, we've set up a map named "users" which contains the names of our
users, but we want to be able to provide more than just the user's name.
Going back to the configuration for the map, we can see that we set the
configuration entry's "nis-value-format" attribute to "%{uid}".  This
attribute holds a format specifier, and changing that format specifier
is how we'll provide more information about the user.

The format specifier syntax is similar to the use of shell variables in
scripts.  Attributes can be referenced and intermixed with literal text.
A simple-but-usable specification for a user entry might look like this:

    %{uid}:%{userPassword}:%{uidNumber}:%{gidNumber}:%{cn}:%{homeDirectory}:%{loginShell}

We shouldn't just use that as-is, though, because it could create a few
problems.  First, as a safety measure, if any attribute which is
referenced in a key or value format isn't part of an entry, that entry
will be ignored.  This is done to prevent empty values from showing up
in unexpected places, for example where the user's numeric UID should
be.  Client software which doesn't check the data could easily treat an
empty numeric UID field as a "0", for example, which would be a problem.

Other attributes, however, should be allowed to not be given.  The
schema for user accounts makes a "loginShell" optional, and in order to
support that, the plugin borrows a bit of shell syntax to allow default
and alternate values to be used.

To make use of the entry's "loginShell" attribute, but to use the value
"/bin/bash" if it doesn't contain that attribute, we can reference it
like so:

   %{loginShell:-/bin/bash}

The default or alternate value is recursively evaluated, so it, too, can
reference other variables.  This is useful for user attributes, such as
the GECOS attribute, which typically contain the user's name:

   %{gecos:-%{cn:-Some Unnamed User}}

Because attributes which hold non-security-sensitive information are
often allowed to be edited by users, a user could attempt to throw
client software off by abusing that ability.  (For example, the fields
in a user's passwd entry are separated by a ":" character, and the
directory server doesn't prohibit that from being used in a "cn"
attribute, so a user could try adding a ":" to their name.)  If the
format specifier looked like this:

    %{uid}:%{userPassword}:%{uidNumber}:%{gidNumber}:%{cn}:%{homeDirectory}:%{loginShell}

The resulting entry value could look like this:

   timtom:*:1000:1000:Tim:Tom 0wns:/home/timtom:/bin/bash

We can prohibit these kinds of shenanigans by configuring the map to
reject any attribute value which contains a particular character, by
setting a "nis-disallowed-chars" value in the map's configuration entry.

    dn: nis-domain=example.com+nis-map=users,cn=NIS Server,cn=plugins,cn=config
    objectclass: extensibleObject
    nis-domain: example.com
    nis-map: users
    nis-base: ou=People, dc=example, dc=com
    nis-filter: (objectClass=posixAccount)
    nis-key-format: %{uid}
    nis-value-format: %{uid}:%{userPassword-:*}:%{uidNumber}:%{gidNumber}:%{gecos:-%{cn:-Some Unnamed User}}:%{homeDirectory}:%{loginShell:-/bin/bash}
    nis-disallowed-chars: :

Format specifiers are described in more detail in "format-specifiers.txt".

== Functions ==

We have another problem: the user has multiple values for the
"userPassword" attribute.  One of them looks like a Unix-style hash:

    {CRYPT}sa3tHJ3/KuYvI

The other is a plaintext password, and we don't want to expose that.
To provide a solution, the plugin's format specifier syntax also
provides a small number of function-like operators.  In this case, we
can use the one called "regsub" to:

    * Try to select one value by using a regular expression.
    * If a match is found, construct the value which will be used.
    * If a match is not found, use a default value.

Depending on whether or not we want a default value, the "regsub"
function takes either three or four arguments:

    %regsub(EXPRESSION,PATTERN,TEMPLATE[,DEFAULT_EXPRESSION])

In this case, selecting the "userPassword" value which matches the
regular expression "^\{CRYPT\}(..*)", building the result using the
first substring match, and providing a default value of "*" if no
matches are found can be done like so:

    %regsub("%{userPassword}","^\{CRYPT\}(..*)","%1","*")

Other matching functions are provided to perform wildcard matches
("match") and regular expression matches ("regmatch").

The parameters passed to functions need to be enclosed in quotation
marks and separated by a ",".

Now our configuration entry will correctly serve any {CRYPT}-style
passwords which are present in entries.

    dn: nis-domain=example.com+nis-map=users,cn=NIS Server,cn=plugins,cn=config
    objectclass: extensibleObject
    nis-domain: example.com
    nis-map: users
    nis-base: ou=People, dc=example, dc=com
    nis-filter: (objectClass=posixAccount)
    nis-key-format: %{uid}
    nis-value-format: %{uid}:%regsub("%{userPassword}","^\{CRYPT\}(..*)","%1","*"):%{uidNumber}:%{gidNumber}:%{gecos:-%{cn:-Some Unnamed User},,,}:%{homeDirectory}:%{loginShell:-/bin/bash}
    nis-disallowed-chars: :,

The module provides more function-like operators than just %regsub(),
but most of those aren't useful when examining user entries.  They turn
out to be more useful when examining entries which represent other types
of information, particularly groups.  They, too, are described in the
"format-specifiers.txt" documentation.

== Defaults ==

In order to save time, the plugin includes compiled-in default entry
filters and default key and value format specifiers which correspond for
commonly-used map names.  Using the default settings is recommended
because they will always be the most correct settings that the project
maintainer(s) know of.  To use the defaults for a map, simply omit them
from the configuration:

    dn: nis-domain=example.com+nis-map=passwd.byname,cn=NIS Server,cn=plugins,cn=config
    objectclass: extensibleObject
    nis-domain: example.com
    nis-map: passwd.byname
    nis-base: ou=People, dc=example, dc=com

    dn: nis-domain=example.com+nis-map=passwd.byuid,cn=NIS Server,cn=plugins,cn=config
    objectclass: extensibleObject
    nis-domain: example.com
    nis-map: passwd.byuid
    nis-base: ou=People, dc=example, dc=com

The above configuration sets up both the "passwd.byname" and
"passwd.byuid" maps using the default key and value specifiers, which
should work correctly for most cases.  Likewise, the module has compiled
into it suitable defaults for a number of common NIS maps.  The full
list is stored in "nis-known-maps.txt".