Subject: bug#56866: [Shepherd] inetd connections not correctly counted?

From: Ludovic Courtès <[email-masked]>
Date: Mon, 01 Aug 2022 10:39:47 +0200

Hi,

We recently experienced a bug on berlin.guix where we’d be locked out of
SSH access because shepherd (0.9.1) would say that the maximum
connection number on the sshd inetd service had been reached.

That threshold is a feature (see ‘max-connections’ in
<openssh-configuration>) but there’s a possibility in this case that a
bug in ‘make-inetd-constructor’ or thereabout led it to get a wrong idea
of the number of active connections.  Unfortunately, we lack syslogs
that would give us info about the time where inetd connections started
accumulating¹.

I tried to come up with a scenario that could lead to that problem with
the test below, to no avail.  If you’ve experienced something similar,
or if you noticed that ‘sshd-*’ services have accumulated on a server of
yours, please let us know!

Thanks,
Ludo’.

¹ That, in turn, was a bug in the rottlog default config, fixed in
  e5a6900baf758a12024283171bf45f2fe90121ee.

diff --git a/tests/inetd.sh b/tests/inetd.sh
index 0301b68..894ce98 100644
--- a/tests/inetd.sh
+++ b/tests/inetd.sh
@@ -77,6 +77,15 @@ cat > "$conf" <<EOF
                                     (endpoint (make-socket-address
                                                AF_UNIX "$service_socket")))
                                    #:max-connections 5)
+   #:stop  (make-inetd-destructor))
+ (make <service>
+   #:provides '(test-inetd-fail)
+   #:start (make-inetd-constructor '("$(type -P false)")
+                                   (list
+                                    (endpoint (make-socket-address
+                                               AF_INET
+                                               INADDR_LOOPBACK
+                                               $PORT))))
    #:stop  (make-inetd-destructor)))
 
 (start 'test-inetd)
@@ -95,6 +104,11 @@ file_descriptor_count ()
     ls -l /proc/$shepherd_pid/fd/[0-9]* | wc -l
 }
 
+# Trigger startup of the finalizer thread, which creates a couple of pipes.
+# That way, those extra file descriptors won't influence the comparison with
+# INITIAL_FD_COUNT done at the end.
+$herd eval root '(gc)'
+
 initial_fd_count=$(file_descriptor_count)
 
 $herd status test-inetd | grep started
@@ -203,3 +217,16 @@ $herd status
 # At this point, shepherd should have INITIAL_FD_COUNT - 1 file descriptors
 # opened.
 test $(file_descriptor_count) -lt $initial_fd_count
+
+# Now test a service that fails as soon as it's passed an incoming connection.
+$herd start test-inetd-fail
+for i in $(seq 1 10)
+do
+    $herd status
+    test $($herd status | grep '\+' | wc -l) -eq 2
+    ! converse_with_echo_server \
+	"(make-socket-address AF_INET INADDR_LOOPBACK $PORT)"
+done
+
+$herd stop test-inetd-unix
+test $(file_descriptor_count) -lt $initial_fd_count