docs: Rework documentation for the NSS module

The page contains some confusing information, especially around
limitations that supposedly only affect one of the two variants,
and goes into what is arguably an unnecessary amount of detail
when it comes to its inner workings.

We can make the page a lot shorter and snappier without
affecting its usefulness, so let's do just that.

Signed-off-by: Andrea Bolognani <abologna@redhat.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
This commit is contained in:
Andrea Bolognani 2024-11-12 20:28:49 +01:00
parent 24580d13d1
commit 3224b25607

View File

@ -1,161 +1,112 @@
.. role:: since
==================
Libvirt NSS module
==================
.. contents::
When it comes to managing guests and executing commands inside them, logging
into guest operating system and doing the job is convenient. Users are used to
ssh in this case. Ideally:
Effectively managing guests often requires connecting to them via SSH, same as
you would for any remote machine. Ideally
``ssh user@virtualMachine``
::
would be nice. But depending on virtual network configuration it might not be
always possible. For instance, when using libvirt NATed network it's dnsmasq
(spawned by libvirt) who assigns IP addresses to domains. But by default, the
dnsmasq process is then not consulted when it comes to host name translation.
Users work around this problem by configuring their libvirt network to assign
static IP addresses and maintaining ``/etc/hosts`` file in sync. But this puts
needless burden onto users. This is where NSS module comes handy.
$ ssh user@mydomain
would work out of the box, but depending on the network configuration that
might not be the case. Setting up the libvirt NSS module is a one-time
operation that makes the process of connecting from the host to the guests
running on it much more convenient.
Note that this feature only works in certain scenarios. See the
`limitations`_ section for more information.
Installation
------------
Installing the module is really easy:
Installing the module on Fedora or RHEL is really easy:
::
# yum install libvirt-nss
# dnf install libvirt-nss
The package might have a different name on other distributions, but the process
of installing it will be similar.
Configuration
-------------
Enabling the module is really easy. Just add **libvirt** into
``/etc/nsswitch.conf`` file. For instance:
To enable the module, modify ``/etc/nsswitch.conf`` so that the ``hosts`` line
looks similar to
::
$ cat /etc/nsswitch.conf
# /etc/nsswitch.conf:
passwd: compat
shadow: compat
group: compat
hosts: files libvirt dns
# ...
hosts: files libvirt libvirt_guest dns
So, in this specific case, whenever ssh program is looking up the host user is
trying to connect to, **files** module is consulted first (which boils down to
looking up the host name in ``/etc/hosts`` file), if not found **libvirt**
module is consulted then. The DNS is the last effort then, if none of the
previous modules matched the host in question. Therefore users should consider
the order in which they want the modules to lookup given host name.
With this configuration, whenever SSH (or any other application)
tries to contact a guest, the ``files`` module will be consulted first (this
boils down to searching for a matching line in ``/etc/hosts``); if no IP
address could be found that way, the ``libvirt`` and ``libvirt_guest`` modules
(see `below <variants_>`__ for differences between the two) will be used
instead. Finally, if no previous attempt at resolving the hostname was
successful, a DNS query will be performed.
Sources of information
Variants
--------
There are two different variants of the module:
* ``libvirt`` (:since:`since 1.3.3`) resolves hostnames based on the
information that the guest OS itself has reported to the DHCP server when
asking for an IP address, so it won't work if the guest OS hasn't been fully
configured yet;
* ``libvirt_guest`` (:since:`since 3.0.0`) resolves hostnames by mapping them
directly to libvirt domain names, so it will work regardless of how the guest
OS is configured and will have more predictable results.
The recommended configuration seen above enables both of them and gives
priority to the former but it's also possible to enable only a single one, or
to alter the precedence by simply changing the order in which they are listed.
Implementation details
----------------------
As of ``v3.0.0`` release, libvirt offers two NSS modules implementing two
different methods of hostname translation. The first and older method is
implemented by ``libvirt`` plugin and basically looks up the hostname to IP
address translation in DHCP server records. Therefore this is dependent on
hostname provided by guests. Thing is, not all the guests out there provide one
in DHCP transactions, or not every sysadmin out there believes all the guests.
Hence libvirt implements second method in ``libvirt_guest`` module which does
libvirt guest name to IP address translation (regardless of hostname set in the
guest).
Whenever a Unix process needs to convert a hostname into an IP address, it will
call the `gethostbyname() <https://linux.die.net/man/3/gethostbyname>`__ libc
function or one of its variants.
To enable either of the modules put their name into the ``nsswitch.conf`` file.
For instance, to enable ``libvirt_guest`` module:
Since multiple sources for this information are possible (for example the
contents of ``/etc/hosts``, DNS, LDAP, etc.) a mechanism called
`NSS <https://en.wikipedia.org/wiki/Name_Service_Switch>`__ has been created to
make the name resolution process extensible. This allows each source to be
implemented as a separate plugin that can be enabled or disabled based on the
administrator's preferences.
In the case of libvirt, the lookup is performed by inspecting the DHCP leases
handed out by ``dnsmasq``, the software used to implement NATed networks. The
results will be the same that would be reported by
::
$ cat /etc/nsswitch.conf
# /etc/nsswitch.conf:
hosts: files libvirt_guest dns
# ...
$ virsh domifaddr --source lease mydomain
Or users can enable both at the same time:
::
$ cat /etc/nsswitch.conf
# /etc/nsswitch.conf:
hosts: files libvirt libvirt_guest dns
# ...
This configuration will mean that if hostname is not found by the ``libvirt``
module (e.g. because a guest did not sent hostname during DHCP transaction), the
``libvirt_guest`` module is consulted (and if the hostname matches libvirt guest
name it will be resolved).
How does it work?
-----------------
Whenever an Unix process wants to do a host name translation
`gethostbyname() <https://linux.die.net/man/3/gethostbyname>`__ or some variant
of it is called. This is a glibc function that takes a string containing the
host name, crunch it and produces a list of IP addresses assigned to that host.
Now, glibc developers made a really good decision when implementing the
internals of the function when they decided to make the function pluggable.
Since there can be several sources for the records (e.g. ``/etc/hosts`` file,
DNS, LDAP, etc.) it would not make much sense to create one big implementation
containing all possible cases. What they have done instead is this pluggable
mechanism. Small plugins implementing nothing but specific technology for lookup
process are provided and the function then calls those plugins. There is just
one configuration file that instructs the lookup function in which order should
the plugins be called and which plugins should be loaded. For more info reading
`wiki page <https://en.wikipedia.org/wiki/Name_Service_Switch>`__ is
recommended.
And this is point where libvirt comes in. Libvirt provides plugin for the NSS
ecosystem. For some time now libvirt keeps a list of assigned IP addresses for
libvirt networks. The NSS plugin does no more than search the list trying to
find matching record for given host name. When found, matching IP address is
returned to the caller. If not found, translation process continues with the
next plugin configured. At this point it is important to stress the order in
which plugins are called. Users should be aware that a hostname might match in
multiple plugins and right after first match, translation process is terminated
and no other plugin is consulted. Therefore, if there are two different records
for the same host name users should carefully chose the lookup order.
except that things will work transparently for any application that uses the
libc resolver, without it needing to link against libvirt or even be aware of
its existence.
Limitations
-----------
#. The ``libvirt`` NSS module matches only hostnames provided by guest. If the
libvirt name and one advertised by guest differs, the latter is matched.
However, as of ``v3.0.0`` there are two libvirt NSS modules translating both
hostnames provided by guest and libvirt guest names.
#. The module works only in that cases where IP addresses are assigned by
dnsmasq spawned by libvirt. Libvirt NATed networks are typical example.
*The following paragraph describes implementation limitation of the ``libvirt``
NSS module.* These limitation are result of libvirt's internal implementation.
While libvirt can report IP addresses regardless of their origin, a public API
must be used to obtain those. However, for the API a connection object is
required. Doing that for every name translation request would be too costly.
Fortunately, libvirt spawns dnsmasq for NATed networks. Not only that, it
provides small executable that on each IP address space change updates an
internal list of addresses thus keeping it in sync. The NSS module then merely
consults the list trying to find the match. Users can view the list themselves:
::
virsh net-dhcp-leases $network
where ``$network`` iterates through all running networks. So the module does
merely the same as
::
virsh domifaddr --source lease $domain
If there's no record for either of the aforementioned commands, it's very likely
that NSS module won't find anything and vice versa. As of ``v3.0.0`` libvirt
provides ``libvirt_guest`` NSS module that doesn't have this limitation.
However, the statement is still true for the ``libvirt`` NSS module.
Since the libvirt NSS module works by looking at ``dnsmasq``, it can only work
for guests that are connected to a NATed libvirt network. Guests that obtain
their IP addresses in any other way (usermode networking, assigned network
devices and so on) will not be able to have their hostnames resolved through
it.
Alternatives
------------
As of ``v10.3.0`` libvirt implements an `SSH proxy <ssh-proxy.html>`__ which
doesn't require any network interface to SSH into the guest as SSH flows
through a VSOCK device.
:since:`Since 10.3.0`, libvirt implements an `SSH proxy <ssh-proxy.html>`__.
This allows the use of SSH even for guests that have no network connectivity,
by communicating over VSOCK.