From: Ken Milmore <ken.milmore@gmail.com>
To: Chuck Lever <chuck.lever@oracle.com>
Cc: kernel-tls-handshake@lists.linux.dev
Subject: Re: ktls-utils client address verification and other patches
Date: Sun, 8 Jun 2025 17:57:03 +0100 [thread overview]
Message-ID: <8342df0b-f308-4b95-905d-87d2c4a4d55a@gmail.com> (raw)
In-Reply-To: <0d1d9550-43cc-4e85-9d88-adb72ffd5dfb@oracle.com>
On 08/06/2025 16:51, Chuck Lever wrote:
> On 6/8/25 6:51 AM, Ken Milmore wrote:
>> I've been evaluating the use of mtls to secure NFS mounts and have noticed there's a fairly serious limitation:
>> Any client which has a valid certificate can impersonate a different client, since the server does not verify client address.
>>
>> This is unfortunate, as I'd like to give different clients access to different NFS shares by naming the clients in /etc/exports,
>> and I'd like to use mtls to ensure that each client not only has a trusted certificate, but that it is only able to access the
>> shares to which it is entitled by /etc/exports. In other words, I want to verify the client host name or address against the
>> client certificate to avoid address spoofing.
>>
>> I can understand why this is not the default behaviour: In general, a server will not be able to verify clients in this way.
>> But for NFS on a personal or corporate LAN, it is highly desirable.
>>
>> To this end, I've developed some patches which allow the tlshd server to be configured to instruct GnuTLS to perform client
>> address verification.
>>
>> Please see patch series here:
>>
>> https://codeberg.org/kbm0/ktls-utils/commits/branch/verification-patchset
>>
>> Because the server can only obtain the client hostname by reverse address lookup, and this has security implications, I have
>> been careful to confirm the hostname first by performing a forward lookup in the manner of FCrDNS: We call getaddrinfo() and
>> check that one of the resulting addresses matches the one which was obtained from the reverse lookup. Should this check fail,
>> we reject the certificate.
>>
>> I've also added an option to use a textualized IP/IPv6 address for verification against the certificate, in cases where no DNS
>> address can be found. This works if the certificate includes the client IP address as an alternative name. It is also possible
>> to use the IP address as the first recourse, and this may be useful in some circumstances - it is possible for client and server
>> to verify each other entirely based on IP addresses with no reliance on DNS, if the certificates are set up correctly.
>>
>> These behaviours are enabled by setting options in tlshd.conf like so:
>>
>> [authenticate.server]
>> verify_peername=true
>> verify_peeraddr=true
>>
>> Another issue I came across involves the fact that when a client connects to a server, the server name is passed to GnuTLS for
>> the purposes of SNI. If the client connects with a bare IP or IPv6 address, this is not strictly allowed, as only DNS names may
>> be used for SNI. This causes the server to reject the transaction. I've added an option to omit the SNI in cases where the client
>> is connecting using an IP address:
>>
>> [authenticate.client]
>> relax_sni=true
>>
>> There are some other patches in the series: I wanted to textualize the peer address up front so it can be used for both logging
>> and also for verification.
>>
>> I also thought some of the processing in netlink.c looked a bit sloppy: Optimistic assumptions were being made about the
>> contents of return buffers when functions like getnameinfo() are called. I have tried to correct these.
>>
>> I am hoping for some informal review or feedback for these patches in the first instance. I didn't want to just spam the mailing
>> list with them upfront. If there is any interest in them, I will submit them formally.
>
> Some general comments:
>
Thanks for replying.
> Essentially we don't want to make certificate validation dependent on
> DNS for any reason, since DNS is easily spoofed. Have you considered
> putting the client's IP address in it's certificate's SAN field?
>
Yes, and my "verify_peeraddr" option covers that. But the current implementation of tlshd does not check either host names or
addresses at all on the server side. Therefore client A can use its own certificate to impersonate client B simply by using client
B's IP address. This is because you are supplying a NULL hostname to gnutls_certificate_verify_peers3() from
tlshd_server_x509_verify_function(). AFAICT this results in no checking of the certificate name at all. I've tested this on my LAN
and it bears out.
Re the insecurity of DNS, yes, I agree it is insecure *in general*. But once you have forward-confirmed an address you are in
essentially the same trust position as any browser connecting to a website via https. I'm not sure why you see that as no better
than no verification at all? The difference, of course, is that in our case we can control the trust store, so we only trust a
limited population of hosts. But what I'm pointing out here is that all trusted hosts are treated identically, which is not very
useful in most setups. I want to check that the client that is connecting to my server actually matches the certificate it is
presenting, otherwise we have an impersonation problem.
Additionally, in the sort of LAN situations where NFS is typically used, name lookups can be secured in a variety of ways.
I'm simply using /etc/hosts, with my systems on an internal domain that has outside lookups (both forward and reverse) on that
domain blocked by dnsmasq, so that is fairly secure. Obviously there is DNSSEC, LDAP and the rest to do this for enterprise setups.
> Further, there will be plenty of cases where clients obtain their IP
> address dynamically. In those cases the server needs to perform its
> certificate validation based solely on what is in the certificate.
In a *general* client/server situation, the client may have an ephemeral IP address, or come in via NAT or a proxy or any number of
ways that mean the server can't reasonably identify the client. In that case, you obviously wouldn't want to turn these options on.
But as I have pointed out above you essentially end up treating all clients as equivalent. You just have to live with that I guess.
But in the specific context of NFS, very often the connection is over a LAN or VPN and the client addresses and names are
meaningful, in fact they are *so* meaningful that this is how access to shares is policed within /etc/exports! So these patches
cover a very natural use case for NFS. I have made everything optional so there is nothing lost (except for the additional security
that you currently don't provide anyway) if you turn the name verification off.
>
> But meanwhile, you can post your patches to this mailing list (inline,
> not as attachments) one at a time, and we can have a look at each
> individual proposal.
>
>
Will do. Thanks!
- Ken. :-)
next prev parent reply other threads:[~2025-06-08 16:57 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-06-08 10:51 ktls-utils client address verification and other patches Ken Milmore
2025-06-08 15:51 ` Chuck Lever
2025-06-08 16:57 ` Ken Milmore [this message]
2025-06-08 17:58 ` Chuck Lever
2025-06-08 18:28 ` Ken Milmore
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=8342df0b-f308-4b95-905d-87d2c4a4d55a@gmail.com \
--to=ken.milmore@gmail.com \
--cc=chuck.lever@oracle.com \
--cc=kernel-tls-handshake@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox