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
|
=== Overview ===
The NIS plugin module's aim is to serve up data from the directory
server using the NIS protocols. It does this by doing what any gateway
would do: it queries the directory server for entries which would
correspond to the contents of maps, reads the contents of various
attributes from those entries, and uses that data to synthesize entries
for maps which it serves to clients.
In broad strokes, it might look like this:
┌──────────┐ NIS ┌───────────┐ LDAP ┌────────────────────┐
│ Client │─────────│ Gateway │──────────│ Directory Server │
└──────────┘ └───────────┘ └────────────────────┘
The links in this diagram represent network traffic. The client uses
the NIS protocol to communicate with the gateway, and the gateway uses
the LDAP protocol to communicate with the directory server.
This implementation requires that the gateway be robust against
variations in directory server availability, be flexible enough to use
any of a number of methods of authenticating to the directory server,
and may additionally require the presence of specific extensions on the
server in order to be able to be even reasonably certain of consistency
with the directory's contents.
In order to sidestep these requirements, and the complexity they add to
an implementation, we decided to implement the gateway as a plugin. As
a plugin, the gateway starts and stops with the directory server, it
does not need to authenticate as a normal client would, and it can be
expected to work with a server which can use it.
Taking just the gateway and directory server portions of the above
diagram, and breaking them down further, we can come to this:
┌──────────────┐ ┌─────────┐ ┌────────────────────────────┐
│ NIS Protocol │───│ Mapping │───│ Directory Server Back Ends │
└──────────────┘ └─────────┘ └────────────────────────────┘
The links in this diagram are all API calls. We've relegated the work
of reading a query (parsed from the NIS client by the NIS Protocol
handler), converting that query to a directory server search operation,
and marshalling the results of that search into a format suitable for
transmission as a NIS response, all to the Mapping module. The
directory server back ends are exposed by SLAPI, of course.
This approach does have its problems, though.
NIS, as a protocol, requires that the server be able to supply a few
bits of information which can't readily (or shouldn't) be retrieved this
way.
NIS requires that a server be able to report a revision number for a
map, akin to the serial number used in a DNS SOA record. A slave server
can use this information to poll for changes in map contents on the
master, possibly beginning a full map enumeration to read those new
contents in order to serve its clients.
A directory server, if it stores revision information at all, stores
it on a per-entry basis. So when a gateway designed as we diagrammed
above is asked for this information, it has at least these options:
a) use an ever-increasing value, such as the current time
- This causes frequent map updates on clients when they don't need
them, and completely unnecessary network traffic.
b) always use the same value
- This keeps clients from ever noticing that a map has changed.
c) return the latest revision of any of the results which formed the
contents of the map
- This could severely load a directory server.
NIS also requires that a server be able to answer whether or not it
services a specified domain, and which maps it serves for a domain that
it serves. While the mapping module could search the directory's
configuration space whenever it is asked these questions, the first
question is asked repeatedly by each running copy of ypbind, which could
also bog servers down (though admittedly, less than the previous case).
If we break the mapping portion up further, we can introduce a map
cache. In this module we can maintain a cache of the NIS server's data
set, taking care to construct it at startup-time, updating it as the
contents of the directory server change, and always serving clients
using data from the cache.
┌──────────────┐ ┌───────────┐ ┌──────────────┐ ┌──────┐
│ NIS Protocol │──│ Map Cache │──│ Map Back End │──│ Data │
└──────────────┘ └───────────┘ └──────────────┘ └──────┘
Which takes us to the current state of the work-in-progress. The NIS
protocol handler reads data from the map cache, and the map back end
uses SLAPI to populate the map cache at startup-time, as well as to
watch for changes in the directory's contents which would need to be
reflected in the map cache.
=== Components ===
This NIS protocol handler module takes the opportunity to set up
listening sockets and register with the local portmapper at module
initialization time. It does so at this point because the directory
server has not yet dropped privileges, and the portmapper will not allow
registrations to unprivileged clients. The plugin then starts a
listening thread to handle NIS clients. Because NIS can be run over
either datagram or connected protocols, the plugin currently services
datagram requests iteratively, but it starts a new thread to handle
requests on behalf of each connected client.
The map cache tracks maps by domain and can quickly answer whether or
not a domain is being served (by whether or not any maps are defined for
it). The current implementation is purely an in-memory one, and is
currently severely unoptimized.
The backend interface module currently populates the map cache at
startup based on the in-directory configuration. It does not yet handle
entry additions, modifications, renames, or removal, so changes to the
directory contents are not at all reflected by the map cache until the
server is restarted. Additionally, the backend interface is currently
hard-coded to use the nisMap/nisObject schema, which while useful for
testing purposes, is not expected to be flexible enough for field use.
|