Nsscash (a pun on cache) is a simple file-based cache for NSS similar to nsscache [1]. The goal is to distribute users/groups/etc. to multiple systems without having to rely on a (single) stable server. Traditional systems like LDAP or NIS require a stable server or users/groups cannot be resolved. By distributing the data to all systems, temporary outages of the server cause no issues on the clients. In addition the local storage is much faster than remote network access. To update the local caches polling via HTTP/HTTPS is performed, for example every minute, only downloading new data if anything has changed.
Nsscash consists of two parts: nsscash
, written in Go, which downloads files
via HTTP or HTTPS, parses them, creates indices and writes the result to a
local file. The second part is the NSS module (libnss_cash.so.2
), written in
C, which provides integration via /etc/nsswitch.conf
. It’s specifically
designed to be very simple and uses the data prepared by nsscash
for
lookups. To support quick lookups, in O(log n), the files utilize indices.
Nsscash can also be used separately from the NSS module to deploy arbitrary files to many hosts and keep them up-to-date.
Nsscash is very careful when deploying the changes:
-
All files are updated using the standard "write to temporary file", "sync", "rename" steps which is atomic on UNIX file systems. The indices are stored in the same file preventing stale data during the update.
-
All errors cause an immediate abort ("fail fast") with a proper error message and a non-zero exit status. This prevents hiding possibly important errors. In addition all files are fetched first and then deployed to try to prevent inconsistent state if only one file can be downloaded. The state file (containing last file modification and content hash) is only updated when all operations were successful.
-
To prevent unexpected permissions,
nsscash
does not create new files. The user must create them first andnsscash
will then re-use the permissions (without the write bits to discourage manual modifications) and owner/group when updating the file (see examples below). -
To prevent misconfigurations, empty files (no users/groups) are not permitted and will not be written to disk. This is designed to prevent the accidental loss of all users/groups on a system.
-
To detect file corruption a hash of all deployed files is stored separately and verified on each
nsscash
run.
The passwd/group files have the following size restrictions:
-
maximum number of entries: '2^64-1' (uint64_t)
-
maximum passwd entry size: 65543 bytes (including newline)
-
maximum group entry size: 65535 bytes (including newline, only one member)
-
maximum members per group: depends on the user name length, with 9 bytes per user: 5460 users
-
nsscash
checks for these restrictions and aborts with an error if they are violated
Nsscash has an extensive test suite for both the Go and C part testing general requirements and various corner cases. See TODO.adoc for a list of known issues and possible improvements.
Nsscash was created as replacement for nsscache [1] because nsscache has very complex code, especially the parts written in C, uses multiple files for data and indices and has had severe bugs (including temporary loss of all users and race conditions when updating data and index files).
Nsscash is licensed under AGPL version 3 or later.
-
Go, for
nsscash
-
github.com/BurntSushi/toml
-
github.com/google/renameio
-
github.com/pkg/errors
-
-
C compiler, for
libnss_cash.so.2
-
HTTP(S) server to provide the passwd/group/etc. files
-
NSS module is only supported on Little-endian systems (for now)
Tested on Debian Buster, but should work on any GNU/Linux system. With adaptations to the NSS module it should work on any UNIX-like system which uses NSS.
Install libnss_cash.so.2
somewhere in your library search path (see
/etc/ld.so.conf
), e.g. /usr/lib/x86_64-linux-gnu/
.
Update /etc/nsswitch.conf
to include the cash module; passwd
and group
are currently supported. For example:
passwd: files cash group: files cash [...]
Create the cache files with the proper permissions (nsscash fetch
won’t
create new files to prevent using incorrect permissions):
touch /etc/passwd.nsscash touch /etc/group.nsscash chmod 0644 /etc/passwd.nsscash chmod 0644 /etc/group.nsscash
Configure the nsscash
configuration file nsscash.toml
, see below.
Then start nsscash
:
nsscash fetch /path/to/config/nsscash.toml
This will fetch the configured files and update the local caches. The files are atomically overwritten (via temporary file, sync, and rename).
Verify the users/groups are available, e.g. with getent
. If everything
works, remember to reboot the host as changes to nsswitch.conf
don’t affect
running processes!
Now configure nsscash
to run regularly, for example via cron or a systemd
timer.
To monitor nsscash
for errors one can use the last modification time of the
state file (see below). It’s written on each successful run and not modified
if an error occurs.
Nsscash is configured through a simple configuration file written in TOML. A typical configuration looks like this:
statepath = "/var/lib/nsscash/state.json"
[[file]] type = "passwd" url = "https://example.org/passwd" path = "/etc/passwd.nsscash"
[[file]] type = "group" url = "https://example.org/group" path = "/etc/group.nsscash"
# Optional, but useful to deploy files which are not supported by the # nsscash NSS module, but by libc's "files" NSS module. nsscash takes care # of the atomic replacement and updates; an "netgroup: files" entry in # "/etc/nsswitch.conf" makes the netgroups available. [[file]] type = "plain" url = "https://example.org/netgroup" path = "/etc/netgroup"
The following global keys are available:
-
statepath
: Path to a JSON file which stores the last modification time and hash of each file; automatically updated bynsscash
. Used to fetch data only when something has changed to reduce the required traffic, viaIf-Modified-Since
. When the hash of a file has changed the download is forced.
Each file
block describes a single file to download/write. The following
keys are available (all keys are required unless marked as optional):
-
type
: Type of this file; can be eitherpasswd
(for files in/etc/passwd
format),group
(for files in/etc/group
format), orplain
(arbitrary format). Onlypasswd
andgroup
files are supported by the nsscash NSS module. But, as explained above,plain
can be used to distribute arbitrary files. The type is required as the.nsscash
files are preprocessed for faster lookups and simpler C code which requires a known format. -
url
: URL to fetch the file from; HTTP and HTTPS are supported -
ca
: Path to a custom CA in PEM format. Restricts HTTPS requests to accept only certificates signed by this CA. Defaults to the system’s certificate store when omitted. (optional) -
username
/password
: Username and password sent via HTTP Basic-Auth to the webserver. The configuration file must not be readable by other users when this key is used. (optional) -
path
: Path to store the retrieved file
Written by Simon Ruderich <simon@ruderich.org>.
This program is licensed under AGPL version 3 or later.
Copyright © 2019-2021 Simon Ruderich
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see https://www.gnu.org/licenses/.