Name Service Switch Service that uses an http JSON backend to authenticate users and groups.
Requires glibc. NSS modules are a glibc mechanism, so this library only works on glibc based distributions (Debian, Ubuntu, Fedora, Arch, ...). It cannot work on musl based distributions such as Alpine: musl has no
nss.h/gshadow.h, exposes no__nss_*loader hooks, and reads/etc/passwd,/etc/groupand/etc/shadowdirectly. Alpine's own/etc/nsswitch.confdocuments that "musl itself does not support NSS" and only honours ahosts:line. Installinggcompatdoes not help, since it provides glibc symbol shims rather than the NSS plugin mechanism.On musl systems the
nss_http_sshkeyhelper still works, because it is an ordinary executable: see SSH Authentication. sshd will however still require the user to exist in/etc/passwd.
- Create a sample users.json and groups.json.
- To create a hashed password you can use
openssl passwd -6
- To create a hashed password you can use
- Spin up a http server that hosts these files
- e.g.
python -m SimpleHTTPServer 8000orpython3 -m http.server 8000
- e.g.
- Compile the library for your system or use a prebuilt version from the Releases page.
- To compile for your system you need
go1.21,makeand the glibc development headers (libc6-devon Debian/Ubuntu). - Run
make installto build and install the library.
- To compile for your system you need
- Make sure you placed the library correctly at
/lib/libnss_http.so.2 - Create a new config at
/etc/nss_http.json:{ "Providers": [ { "Name": "http", "URLs": { "Users": "http://localhost:8000/users.json", "Groups": "http://localhost:8000/groups.json" }, "RequestTimeout": "1m", "Headers": {} } ], "Cache": { "Name": "disabled" }, "AllowListingOfUsers": false, "AllowListingOfGroups": false } - Adjust
/etc/nsswitch.confto includehttpforpasswd,shadowandgroup:# /etc/nsswitch.conf passwd: compat http group: compat http shadow: compat http gshadow: files http ... - Test the functionality using
getent passwd <username>
A user's primary group comes from the Gid field on the user. Supplementary
group membership comes from the GroupMembers list on a group:
[
{ "Name": "joe", "Passwd": "", "Gid": 3000, "GroupMembers": [] },
{ "Name": "admins", "Passwd": "", "Gid": 6000, "GroupMembers": ["joe"] }
]Verify both with:
$ getent group admins
admins:x:6000:joe
$ id joe
uid=3000(joe) gid=3000(joe) groups=3000(joe),6000(admins)Note that /etc/nsswitch.conf is consulted in order, so a group that also
exists in /etc/group (for example staff, gid 50 on Debian) will resolve to
the local entry instead of the HTTP one.
It is possible to add ssh authentication to the system by altering the sshd_config:
AuthorizedKeysCommand /sbin/nss_http_sshkey
AuthorizedKeysCommandUser nobody
nss_http will write a log file to /var/log/nss_http.log.
You can change the log file path by specifying NSS_HTTP_LOG_FILE,
setting it to disabled will disable the log file.
(notice you could also use /dev/stdout or /dev/stderr as a value).
You can also enable debug logging by setting NSS_HTTP_DEBUG to true.
Configuration lives at /etc/nss_http.json.
It is an ordinary json file that specifies the providers to use to look up
users and groups.
{
"Providers": [
Provider...
],
"Cache": Cache,
"AllowListingOfUsers": false,
"AllowListingOfGroups": false,
"DisableShadow": false
}Notice that you can specify multiple providers, but only one cache provider.
nss_http will always follow the order of the specified providers to look up users and groups, however, it will always
try to lookup users and groups by cache first.
You can disallow the listing of all users and groups, this is especially useful if you deal with a lot of users and groups. Or only have a http server that returns individual users and groups.
By default, all passwords need to be hashed upfront in the crypt(3) format:
$<id>[$<param>=<value>(,<param>=<value>)*][$<salt>[$<hash>]]
Depending on your system, you can use openssl passwd -6 to hash the passwords upfront.
You could also use plain text mode by setting DisableShadow to true, but this is not recommended.
The http provider allows you to lookup users and groups using http.
You can either specify only two endpoints pointing to a users.json and groups.json file.
Or point directly to individual resources. (more in the example section).
You could also specify headers in the headers section, use that to specify a token using
the Authorization header or similar.
In this example we only point to a list of users and groups, nss_http will automatically pull the correct user and group outside of this list.
{
"Providers": [
{
"Name": "http",
"URLs": {
"Users": "http://localhost:800/users.json",
"Groups": "http://localhost:800/groups.json"
},
"RequestTimeout": "1m",
"Headers": {}
}
],
"Cache": {
"Name": "disabled"
},
"AllowListingOfUsers": false,
"AllowListingOfGroups": false,
"DisableShadow": false
}A more resource efficient variant is to directly point to individual resources.
When a lookup will be performed the id or name of the user/group will be appended.
In this example getent passwd joe will result calling http://localhost:800/user/name/joe.
{
"Providers": [
{
"Name": "http",
"URLs": {
"UserUID": "http://localhost:800/user/uid/",
"UserName": "http://localhost:800/user/name/",
"GroupGID": "http://localhost:800/group/gid/",
"GroupName": "http://localhost:800/group/name/"
},
"RequestTimeout": "1m",
"Headers": {}
}
],
"Cache": {
"Name": "disabled"
},
"AllowListingOfUsers": false,
"AllowListingOfGroups": false,
"DisableShadow": false
}Notice that a list endpoint is required when you want to allow listing of users and groups using
AllowListingOfUsersandAllowListingOfGroups.
You could also specify the list and individual endpoints.
nss_http will lookup specific users/groups via the individual endpoints,
list requests will go directly to the users/groups endpoint.
{
"Providers": [
{
"Name": "http",
"URLs": {
"Users": "http://localhost:800/users",
"UserUID": "http://localhost:800/user/uid/",
"UserName": "http://localhost:800/user/name/",
"Groups": "http://localhost:800/groups",
"GroupGID": "http://localhost:800/group/gid/",
"GroupName": "http://localhost:800/group/name/"
},
"RequestTimeout": "1m",
"Headers": {}
}
],
"Cache": {
"Name": "disabled"
},
"AllowListingOfUsers": false,
"AllowListingOfGroups": false,
"DisableShadow": false
}Notice that a list endpoint is required when you want to allow listing of users and groups using
AllowListingOfUsersandAllowListingOfGroups.
For the Users endpoint a list of users is expected as a result.
You can see an example in the users.json file.
For the Groups endpoint a list of groups is expected as a result.
You can see an example in the groups.json file.
When a specific user is requested, make sure to respond with http status code 200 and return json content:
{
"User": "joe",
"Passwd": "$6$.WdgkoyPbvxIDDKU$mOVy8BlNvGssTojiLDyo37S7/puNMBx53S4VAp1nhxSnV5G7bzZw42QxbcYiq4TJwReY0cBLQGc5Dt6Mnk4lg1",
"Name": "Joe Doe",
"Dir": "/home/joe",
"Shell": "/bin/bash",
"Uid": 3000,
"Gid": 3000
}If you want to signal that the requested user does not exist return the http status code 404.
When a group is requested, make sure to respond with http status code 200 and return json content:
{
"Name": "admins",
"Passwd": "",
"Gid": 6000,
"GroupMembers": ["joe"]
}If you want to signal that the requested group does not exist return the http status code 404.
Notice that in
GroupMembersyou only specify users which will have this group as a secondary group, the primary group information is already present in the user data.
Lookup a specific user or group by using redis key value.
Based on the use case following keys will be used:
| use case | key |
|---|---|
| lookup a user by name | users/name/<name> |
| lookup a user by uid | users/id/<uid> |
| lookup a group by name | groups/name/<name> |
| lookup a group by uid | groups/id/<gid> |
Notice that the data must be encoded in json.
{
"Providers": [
{
"Name": "redis",
"URL": "redis://myusername:mypassword@localhost:6379"
}
],
"Cache": {
"Name": "disabled"
},
"AllowListingOfUsers": false,
"AllowListingOfGroups": false,
"DisableShadow": false
}The file provider allows you to lookup users and groups using a json file.
You need to specify a users.json and groups.json file.
{
"Providers": [
{
"Name": "file",
"Users": "/etc/nss_http/users.json",
"Groups": "/etc/nss_http/groups.json"
}
],
"Cache": {
"Name": "disabled"
},
"AllowListingOfUsers": false,
"AllowListingOfGroups": false,
"DisableShadow": false
}Disable cache entirely, no data will be cached.
{
"Cache": {
"Name": "disabled"
}
}Cache using redis.
Uses the same format as the redis provider.
{
"Cache": {
"Name": "redis",
"URL": "redis://myusername:mypassword@localhost:6379",
"TTL": "1m"
}
}You don't have to use the users and groups functionality of nss_http.
Simply remove http from the nsswitch.conf file where you don't want to use it.
And remove the urls in the http provider.