unofficial mirror of bug-guix@gnu.org 
 help / color / mirror / code / Atom feed
* bug#55335: openssh-service no longer listens on IPv6
@ 2022-05-09 10:39 Christopher Baines
  2022-05-13 12:21 ` Christopher Baines
  2022-05-13 14:23 ` bug#55335: [PATCH] services: Allow shepherd to listen for IPv6 connections to openssh Christopher Baines
  0 siblings, 2 replies; 20+ messages in thread
From: Christopher Baines @ 2022-05-09 10:39 UTC (permalink / raw)
  To: 55335

[-- Attachment #1: Type: text/plain, Size: 1345 bytes --]

This looks to be a recent regression, probably connected with the
shepherd now doing the listening, rather than sshd itself.

Previously, you could use both IPv4 and IPv6.

  netstat -tlnp | grep sshd
  tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      26683/sshd: /gnu/st
  tcp6       0      0 :::22                   :::*                    LISTEN      26683/sshd: /gnu/st

Now though, it looks like with shepherd doing the listening, you can
only use IPv4.

  netstat -tlnp | grep 22
  tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      1/guile


On an affected machine, you can reproduce this by trying to SSH over v6.

  cbaines@lakeside ~$ ssh 127.0.0.1
  The authenticity of host '127.0.0.1 (127.0.0.1)' can't be established.
  ED25519 key fingerprint is SHA256:1wV7w84awrGv5ilP5e8k5ygIvSkXSJ6LIy3MnqZG2Jw.
  This key is not known by any other names
  Are you sure you want to continue connecting (yes/no/[fingerprint])? ^C

  cbaines@lakeside ~$ ssh ::1
  ssh: connect to host ::1 port 22: Connection refused


This isn't an issue if you're not using IPv6, but if you have a machine
only accessible via IPv6, then you can't ssh in. The main workaround
I've found is getting access via other means, then starting sshd
listening on a different port (as the shepherd is using 22).

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 987 bytes --]

^ permalink raw reply	[flat|nested] 20+ messages in thread

* bug#55335: openssh-service no longer listens on IPv6
  2022-05-09 10:39 bug#55335: openssh-service no longer listens on IPv6 Christopher Baines
@ 2022-05-13 12:21 ` Christopher Baines
  2022-05-13 14:23 ` bug#55335: [PATCH] services: Allow shepherd to listen for IPv6 connections to openssh Christopher Baines
  1 sibling, 0 replies; 20+ messages in thread
From: Christopher Baines @ 2022-05-13 12:21 UTC (permalink / raw)
  To: 55335

[-- Attachment #1: Type: text/plain, Size: 2723 bytes --]


Christopher Baines <mail@cbaines.net> writes:

> This looks to be a recent regression, probably connected with the
> shepherd now doing the listening, rather than sshd itself.
>
> Previously, you could use both IPv4 and IPv6.
>
>   netstat -tlnp | grep sshd
>   tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      26683/sshd: /gnu/st
>   tcp6       0      0 :::22                   :::*                    LISTEN      26683/sshd: /gnu/st
>
> Now though, it looks like with shepherd doing the listening, you can
> only use IPv4.
>
>   netstat -tlnp | grep 22
>   tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      1/guile
>
>
> On an affected machine, you can reproduce this by trying to SSH over v6.
>
>   cbaines@lakeside ~$ ssh 127.0.0.1
>   The authenticity of host '127.0.0.1 (127.0.0.1)' can't be established.
>   ED25519 key fingerprint is SHA256:1wV7w84awrGv5ilP5e8k5ygIvSkXSJ6LIy3MnqZG2Jw.
>   This key is not known by any other names
>   Are you sure you want to continue connecting (yes/no/[fingerprint])? ^C
>
>   cbaines@lakeside ~$ ssh ::1
>   ssh: connect to host ::1 port 22: Connection refused
>
>
> This isn't an issue if you're not using IPv6, but if you have a machine
> only accessible via IPv6, then you can't ssh in. The main workaround
> I've found is getting access via other means, then starting sshd
> listening on a different port (as the shepherd is using 22).

I've had another look at how this might be fixed.

One workaround that seems to work is having the service just listen on
an IPv6 socket as I believe Linux translates IPv4 connections to
IPv6. The openssh system test seems to pass, and I believe this would
fix not being able to connect over IPv6, although it seems likely that
this would break things relying on IPv4 usage, like configuration based
on specific IP addresses.

I think the more rigerous approach would be to have shepherd listen on
two sockets, one for IPv4 and another for IPv6. That's currently
difficult though because of the above behaviour, the IPv6 socket blocks
opening the IPv4 one. I've got a patch [1] to Guile that adds the
constants needed for the setsockopt call and once that's possible, I
believe the setsockopt call would need to happen in
make-inetd-constructor.

1: https://lists.gnu.org/archive/html/guile-devel/2022-05/msg00007.html

Without reverting to the previous behaviour, maybe the best way forward
is to at least allow having the service listen via IPv6. That would mean
those affected by the loss of IPv6 support could enable it, and would
hopefully avoid breaking anyones configuration where they're relying on
native IPv4 connections. I'll send a patch for this shortly.

Chris

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 987 bytes --]

^ permalink raw reply	[flat|nested] 20+ messages in thread

* bug#55335: [PATCH] services: Allow shepherd to listen for IPv6 connections to openssh.
  2022-05-09 10:39 bug#55335: openssh-service no longer listens on IPv6 Christopher Baines
  2022-05-13 12:21 ` Christopher Baines
@ 2022-05-13 14:23 ` Christopher Baines
  2022-05-13 15:23   ` Jack Hill
                     ` (2 more replies)
  1 sibling, 3 replies; 20+ messages in thread
From: Christopher Baines @ 2022-05-13 14:23 UTC (permalink / raw)
  To: 55335

Prior to the switch to the openssh service using inetd, you could connect over
IPv4 or IPv6. With inetd, you can only connect over IPv4, meaning for machines
with just IPv6 connectivity, you can't connect.

Switching to listing via IPv6 should support IPv4 connections, as Linux is
capable of translating IPv4 connections to IPv6. I think there's a risk that
switching to this approach will affect some uses of the openssh
service. Therefore, this commit makes this a configuration option, which is #f
by default.

In the future, once it's easy to do so via Guile and the shepherd, it would be
good if two sockets were used, one for IPv4 and one for IPv6. That's not easy
at the moment, as the IPv6 socket conflicts with the IPv4 one, due to the
translation behaviour described above.

* gnu/services/ssh.scm (openssh-listen-via-ipv6?): New procedure.
(openssh-shepherd-service): Factor in listen-via-ipv6? when constructing the
socket address.
* doc/guix.texi (Networking Services): Document the new listen-via-ipv6?
field.
---
 doc/guix.texi        |  9 +++++++++
 gnu/services/ssh.scm | 13 +++++++++++--
 2 files changed, 20 insertions(+), 2 deletions(-)

diff --git a/doc/guix.texi b/doc/guix.texi
index c168a66072..b168cb379e 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -19119,6 +19119,15 @@ Match Address 192.168.0.1
   PermitRootLogin yes"))
 @end lisp
 
+@item @code{listen-via-ipv6?} (default: @code{#f})
+When listening via a inetd-style Shepherd service, connections will only
+be accepted via IPv4.
+
+To have the shepherd listen instead via IPv6, set this option to
+#t. Depending on how network connections are handled, this will either
+enable connecting via IPv6 and translated IPv4, or just enable IPv6
+connections only.
+
 @end table
 @end deftp
 
diff --git a/gnu/services/ssh.scm b/gnu/services/ssh.scm
index 7fbbe383e5..427f0e4739 100644
--- a/gnu/services/ssh.scm
+++ b/gnu/services/ssh.scm
@@ -363,7 +363,13 @@ (define-record-type* <openssh-configuration>
   ;; proposed in <https://bugs.gnu.org/27155>.  Keep it internal/undocumented
   ;; for now.
   (%auto-start?          openssh-auto-start?
-                         (default #t)))
+                         (default #t))
+
+  ;; Boolean
+  ;; XXX: The service should really listen via IPv4 and IPv6 by default, but
+  ;; this is a little tricky. See https://issues.guix.gnu.org/55335
+  (listen-via-ipv6?      openssh-listen-via-ipv6?
+                         (default #f)))
 
 (define %openssh-accounts
   (list (user-group (name "sshd") (system? #t))
@@ -535,7 +541,10 @@ (define openssh-command
          (start #~(if (defined? 'make-inetd-constructor)
                       (make-inetd-constructor
                        (append #$openssh-command '("-i"))
-                       (make-socket-address AF_INET INADDR_ANY
+                       (make-socket-address #$(if (openssh-listen-via-ipv6? config)
+                                                  #~AF_INET6
+                                                  #~AF_INET)
+                                            INADDR_ANY
                                             #$port-number)
                        #:max-connections #$max-connections)
                       (make-forkexec-constructor #$openssh-command
-- 
2.34.0





^ permalink raw reply related	[flat|nested] 20+ messages in thread

* bug#55335: [PATCH] services: Allow shepherd to listen for IPv6 connections to openssh.
  2022-05-13 14:23 ` bug#55335: [PATCH] services: Allow shepherd to listen for IPv6 connections to openssh Christopher Baines
@ 2022-05-13 15:23   ` Jack Hill
  2022-05-13 15:25     ` Jack Hill
  2022-05-14  8:42     ` bug#55335: openssh-service no longer listens on IPv6 Ludovic Courtès
  2022-05-14 14:16   ` Ludovic Courtès
  2022-05-14 15:49   ` Ludovic Courtès
  2 siblings, 2 replies; 20+ messages in thread
From: Jack Hill @ 2022-05-13 15:23 UTC (permalink / raw)
  To: Christopher Baines; +Cc: 55335

Thanks for looking into this! Does this fix work for you (I assume so)? I 
tried a simpler patch to use a v6 socket:


--- a/gnu/services/ssh.scm
+++ b/gnu/services/ssh.scm
@@ -535,7 +535,7 @@ (define openssh-command
           (start #~(if (defined? 'make-inetd-constructor)
                        (make-inetd-constructor
                         (append #$openssh-command '("-i"))
-                       (make-socket-address AF_INET INADDR_ANY
+                       (make-socket-address AF_INET6 INADDR_ANY
                                              #$port-number)
                         #:max-connections #$max-connections)
                        (make-forkexec-constructor #$openssh-command

and that does indeed produce a v6 socket that also accepts v4 connection. 
The output of `ss -tulpen`:

tcp       LISTEN     0          10 
*:22                       *:*         users:(("shepherd",pid=1,fd=29)) ino:1522146 sk:2001 cgroup:/ v6only:0 <->

However, while ssh is now able to connect to the socket, something is 
going wrong in the handoff to sshd. I see the following message printed on 
the console when trying to connect:

Uncaught exception in task:
In fibers.scm:
   150:8 4 (_)
In shepherd/service.scm:
   1435:21 3 (_)
   1280:30 2 (socket-address->string #(10 # 37896 0 0))
In unknown file:
           1 (inet-ntop 2 42540578165178177408896616697074944157)
In ice-9/boot-9.scm:
   1685:16 0 (raise-exception _ #:continualbe? _)
ice-9/boot-9.scm:1685:16: In procecure raise-exception:
Value our of range 0 to 18446744073709551615: 42540578165178177408896616697074944157

Best,
Jack




^ permalink raw reply	[flat|nested] 20+ messages in thread

* bug#55335: [PATCH] services: Allow shepherd to listen for IPv6 connections to openssh.
  2022-05-13 15:23   ` Jack Hill
@ 2022-05-13 15:25     ` Jack Hill
  2022-05-14  8:42     ` bug#55335: openssh-service no longer listens on IPv6 Ludovic Courtès
  1 sibling, 0 replies; 20+ messages in thread
From: Jack Hill @ 2022-05-13 15:25 UTC (permalink / raw)
  To: Christopher Baines; +Cc: 55335

On Fri, 13 May 2022, Jack Hill wrote:

> Thanks for looking into this! Does this fix work for you (I assume so)? I 
> tried a simpler patch to use a v6 socket:
>
>
> --- a/gnu/services/ssh.scm
> +++ b/gnu/services/ssh.scm
> @@ -535,7 +535,7 @@ (define openssh-command
>          (start #~(if (defined? 'make-inetd-constructor)
>                       (make-inetd-constructor
>                        (append #$openssh-command '("-i"))
> -                       (make-socket-address AF_INET INADDR_ANY
> +                       (make-socket-address AF_INET6 INADDR_ANY
>                                             #$port-number)
>                        #:max-connections #$max-connections)
>                       (make-forkexec-constructor #$openssh-command
>
> and that does indeed produce a v6 socket that also accepts v4 connection. The 
> output of `ss -tulpen`:
>
> tcp       LISTEN     0          10 *:22                       *:* 
> users:(("shepherd",pid=1,fd=29)) ino:1522146 sk:2001 cgroup:/ v6only:0 <->
>
> However, while ssh is now able to connect to the socket, something is going 
> wrong in the handoff to sshd. I see the following message printed on the 
> console when trying to connect:
>
> Uncaught exception in task:
> In fibers.scm:
>  150:8 4 (_)
> In shepherd/service.scm:
>  1435:21 3 (_)
>  1280:30 2 (socket-address->string #(10 # 37896 0 0))
> In unknown file:
>          1 (inet-ntop 2 42540578165178177408896616697074944157)
> In ice-9/boot-9.scm:
>  1685:16 0 (raise-exception _ #:continualbe? _)
> ice-9/boot-9.scm:1685:16: In procecure raise-exception:
> Value our of range 0 to 18446744073709551615: 
> 42540578165178177408896616697074944157
>
> Best,
> Jack

I should have specified: now neither v4 or v6 work.

Best,
Jack




^ permalink raw reply	[flat|nested] 20+ messages in thread

* bug#55335: openssh-service no longer listens on IPv6
  2022-05-13 15:23   ` Jack Hill
  2022-05-13 15:25     ` Jack Hill
@ 2022-05-14  8:42     ` Ludovic Courtès
  1 sibling, 0 replies; 20+ messages in thread
From: Ludovic Courtès @ 2022-05-14  8:42 UTC (permalink / raw)
  To: Jack Hill; +Cc: Christopher Baines, 55335

Hi,

Jack Hill <jackhill@jackhill.us> skribis:

> However, while ssh is now able to connect to the socket, something is
> going wrong in the handoff to sshd. I see the following message
> printed on the console when trying to connect:
>
> Uncaught exception in task:
> In fibers.scm:
>   150:8 4 (_)
> In shepherd/service.scm:
>   1435:21 3 (_)
>   1280:30 2 (socket-address->string #(10 # 37896 0 0))
> In unknown file:
>           1 (inet-ntop 2 42540578165178177408896616697074944157)
> In ice-9/boot-9.scm:
>   1685:16 0 (raise-exception _ #:continualbe? _)
> ice-9/boot-9.scm:1685:16: In procecure raise-exception:
> Value our of range 0 to 18446744073709551615: 42540578165178177408896616697074944157

Oops, another embarrassing bug, now fixed in Shepherd commit
27dd4df9d83e9c59668bd9e6ca05a3a4983e10d2.

Thanks,
Ludo’.




^ permalink raw reply	[flat|nested] 20+ messages in thread

* bug#55335: openssh-service no longer listens on IPv6
  2022-05-13 14:23 ` bug#55335: [PATCH] services: Allow shepherd to listen for IPv6 connections to openssh Christopher Baines
  2022-05-13 15:23   ` Jack Hill
@ 2022-05-14 14:16   ` Ludovic Courtès
  2022-05-18 14:06     ` bug#55335: [PATCH Shepherd 0/3] Endpoints for inetd services + IPv6-only endpoints Ludovic Courtès
  2022-05-14 15:49   ` Ludovic Courtès
  2 siblings, 1 reply; 20+ messages in thread
From: Ludovic Courtès @ 2022-05-14 14:16 UTC (permalink / raw)
  To: Christopher Baines; +Cc: 55335

Hi,

Christopher Baines <mail@cbaines.net> skribis:

> Prior to the switch to the openssh service using inetd, you could connect over
> IPv4 or IPv6. With inetd, you can only connect over IPv4, meaning for machines
> with just IPv6 connectivity, you can't connect.
>
> Switching to listing via IPv6 should support IPv4 connections, as Linux is
> capable of translating IPv4 connections to IPv6. I think there's a risk that
> switching to this approach will affect some uses of the openssh
> service. Therefore, this commit makes this a configuration option, which is #f
> by default.
>
> In the future, once it's easy to do so via Guile and the shepherd, it would be
> good if two sockets were used, one for IPv4 and one for IPv6. That's not easy
> at the moment, as the IPv6 socket conflicts with the IPv4 one, due to the
> translation behaviour described above.

Yes, I was going to suggest turning the ‘address’ argument of
‘make-inetd-constructor’ into ‘addresses’ (plural), with backward
compatibility.  For sshd, we’d do:

         (make-inetd-constructor
          (append #$openssh-command '("-i"))
          (list (make-socket-address AF_INET INADDR_ANY #$port-number)
                (make-socket-address AF_INET6 INADDR_ANY #$port-number)))

It’s not that simple, due to the v6-to-v4 translation you mention:

--8<---------------cut here---------------start------------->8---
scheme@(guile-user)> (define v4 (make-socket-address AF_INET INADDR_ANY 5555))
scheme@(guile-user)> (define v6 (make-socket-address AF_INET6 INADDR_ANY 5555))
scheme@(guile-user)> (define s4 (socket AF_INET SOCK_STREAM 0))
scheme@(guile-user)> (define s6 (socket AF_INET6 SOCK_STREAM 0))
scheme@(guile-user)> (bind s4 v4)
scheme@(guile-user)> (bind s6 v6)
ice-9/boot-9.scm:1685:16: In procedure raise-exception:
In procedure bind: Address already in use

Entering a new prompt.  Type `,bt' for a backtrace or `,q' to continue.
--8<---------------cut here---------------end--------------->8---

… but it can be made to work:

--8<---------------cut here---------------start------------->8---
scheme@(guile-user)> (define s4 (socket AF_INET SOCK_STREAM 0))
scheme@(guile-user)> (define s6 (socket AF_INET6 SOCK_STREAM 0))
scheme@(guile-user)> (define IPPROTO_IPV6 41)
scheme@(guile-user)> (define IPV6_V6ONLY 26)
scheme@(guile-user)> (setsockopt s6 IPPROTO_IPV6 IPV6_V6ONLY 1)
scheme@(guile-user)> (bind s4 v4)
scheme@(guile-user)> (bind s6 v6)
--8<---------------cut here---------------end--------------->8---

So ‘make-inetd-constructor’ would interpret v6 addresses as v6-only,
with the understanding that the caller has to explicitly pass all the
relevant addresses.

Thoughts?

We could release Shepherd shortly with the fixes that have accumulated.
The service in Guix would be able to use it, but only if PID 1 is recent
enough.

Thanks,
Ludo’.




^ permalink raw reply	[flat|nested] 20+ messages in thread

* bug#55335: openssh-service no longer listens on IPv6
  2022-05-13 14:23 ` bug#55335: [PATCH] services: Allow shepherd to listen for IPv6 connections to openssh Christopher Baines
  2022-05-13 15:23   ` Jack Hill
  2022-05-14 14:16   ` Ludovic Courtès
@ 2022-05-14 15:49   ` Ludovic Courtès
  2022-05-14 19:09     ` Jack Hill
  2022-05-17 21:33     ` Christopher Baines
  2 siblings, 2 replies; 20+ messages in thread
From: Ludovic Courtès @ 2022-05-14 15:49 UTC (permalink / raw)
  To: Christopher Baines; +Cc: 55335

Hi,

Christopher Baines <mail@cbaines.net> skribis:

> Switching to listing via IPv6 should support IPv4 connections, as Linux is
> capable of translating IPv4 connections to IPv6. I think there's a risk that
> switching to this approach will affect some uses of the openssh
> service. Therefore, this commit makes this a configuration option, which is #f
> by default.

[...]

> +                       (make-socket-address #$(if (openssh-listen-via-ipv6? config)
> +                                                  #~AF_INET6
> +                                                  #~AF_INET)
> +                                            INADDR_ANY
>                                              #$port-number)

Thinking about it, what do you think is the risk of using AF_INET6
unconditionally?

AFAICS it just works.  Is there a switch somewhere that might affect
that behavior?

(I still think that changing ‘make-inetd-constructor’ to accept multiple
addresses is a better fix longer-term, but if we can have this quick
fix, that’s great.)

Ludo’.




^ permalink raw reply	[flat|nested] 20+ messages in thread

* bug#55335: openssh-service no longer listens on IPv6
  2022-05-14 15:49   ` Ludovic Courtès
@ 2022-05-14 19:09     ` Jack Hill
  2022-05-17 21:33     ` Christopher Baines
  1 sibling, 0 replies; 20+ messages in thread
From: Jack Hill @ 2022-05-14 19:09 UTC (permalink / raw)
  To: Ludovic Courtès; +Cc: Christopher Baines, 55335

[-- Attachment #1: Type: text/plain, Size: 556 bytes --]

On Sat, 14 May 2022, Ludovic Courtès wrote:

> Hi,
>
> Thinking about it, what do you think is the risk of using AF_INET6
> unconditionally?
>
> AFAICS it just works.  Is there a switch somewhere that might affect
> that behavior?

Yes, I beleive that it's in sysctl:

```
$ sysctl net.ipv6.bindv6only
net.ipv6.bindv6only = 0
```

If enabled, the v6 socket wouldn't work for v4. Disabled is the default on 
Guix System. I don't know what would happen if v6 were disabled entirely. 
Hopefully that's not something we have to worry about in 2022.

HTH,
Jack

^ permalink raw reply	[flat|nested] 20+ messages in thread

* bug#55335: openssh-service no longer listens on IPv6
  2022-05-14 15:49   ` Ludovic Courtès
  2022-05-14 19:09     ` Jack Hill
@ 2022-05-17 21:33     ` Christopher Baines
  2022-05-18  9:30       ` Ludovic Courtès
  1 sibling, 1 reply; 20+ messages in thread
From: Christopher Baines @ 2022-05-17 21:33 UTC (permalink / raw)
  To: Ludovic Courtès; +Cc: 55335

[-- Attachment #1: Type: text/plain, Size: 1546 bytes --]


Ludovic Courtès <ludo@gnu.org> writes:

> Hi,
>
> Christopher Baines <mail@cbaines.net> skribis:
>
>> Switching to listing via IPv6 should support IPv4 connections, as Linux is
>> capable of translating IPv4 connections to IPv6. I think there's a risk that
>> switching to this approach will affect some uses of the openssh
>> service. Therefore, this commit makes this a configuration option, which is #f
>> by default.
>
> [...]
>
>> +                       (make-socket-address #$(if (openssh-listen-via-ipv6? config)
>> +                                                  #~AF_INET6
>> +                                                  #~AF_INET)
>> +                                            INADDR_ANY
>>                                              #$port-number)
>
> Thinking about it, what do you think is the risk of using AF_INET6
> unconditionally?

I'm assuming that configuration that looks at the IP addresses will be
affected, e.g. things like:

  Match Address 127.0.0.*
    PubkeyAuthentication yes

But this is just a guess.

> AFAICS it just works.  Is there a switch somewhere that might affect
> that behavior?
>
> (I still think that changing ‘make-inetd-constructor’ to accept multiple
> addresses is a better fix longer-term, but if we can have this quick
> fix, that’s great.)

I'm also interested in a quick fix. I'd like to either make the switch
to using AF_INET6 unconditionally, or push the patch I sent for allowing
it to be used through a configuration option.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 987 bytes --]

^ permalink raw reply	[flat|nested] 20+ messages in thread

* bug#55335: openssh-service no longer listens on IPv6
  2022-05-17 21:33     ` Christopher Baines
@ 2022-05-18  9:30       ` Ludovic Courtès
  0 siblings, 0 replies; 20+ messages in thread
From: Ludovic Courtès @ 2022-05-18  9:30 UTC (permalink / raw)
  To: Christopher Baines; +Cc: 55335

Hi Chris,

Christopher Baines <mail@cbaines.net> skribis:

> I'm also interested in a quick fix. I'd like to either make the switch
> to using AF_INET6 unconditionally, or push the patch I sent for allowing
> it to be used through a configuration option.

How about going with unconditional AF_INET6 for now?  That way we
wouldn’t have that new option that will likely become a no-op
afterwards.

I’ll propose changes to the Shepherd soon, so we can fix it for good.

Thanks,
Ludo’.




^ permalink raw reply	[flat|nested] 20+ messages in thread

* bug#55335: [PATCH Shepherd 0/3] Endpoints for inetd services + IPv6-only endpoints
  2022-05-14 14:16   ` Ludovic Courtès
@ 2022-05-18 14:06     ` Ludovic Courtès
  2022-05-18 14:06       ` bug#55335: [PATCH Shepherd 1/3] service: 'make-inetd-constructor' accepts a list of endpoints Ludovic Courtès
                         ` (2 more replies)
  0 siblings, 3 replies; 20+ messages in thread
From: Ludovic Courtès @ 2022-05-18 14:06 UTC (permalink / raw)
  To: 55335; +Cc: Ludovic Courtès, Jack Hill, Christopher Baines

Hi!

Here’s a couple of changes to the Shepherd addressing the concerns
Chris raised in <https://issues.guix.gnu.org/55335>:

  • ‘make-inetd-constructor’ now accepts a list of endpoints, like
    ‘make-systemd-constructor’, instead of a single address.

  • AF_INET6 endpoints are now interpreted as IPv6-only.

I’ve pushed this in the Shepherd repo as ‘wip-inetd-ipv6’.  You’re
welcome to test that branch in Guix System VMs or something.

Lemme know what you think!  If it’s good, we can merge it and
release the Shepherd 0.9.1 with this and other fixes that have
accumulated.

Ludo’.

Ludovic Courtès (3):
  service: 'make-inetd-constructor' accepts a list of endpoints.
  tests: Update inetd tests to pass a list of endpoints.
  Interpret AF_INET6 endpoints as IPv6-only.

 NEWS                           |  24 +++
 configure.ac                   |  12 ++
 doc/shepherd.texi              |  68 ++++----
 modules/shepherd/service.scm   | 274 +++++++++++++++++----------------
 modules/shepherd/system.scm.in |  11 ++
 tests/inetd.sh                 |  71 ++++++++-
 6 files changed, 294 insertions(+), 166 deletions(-)


base-commit: 05f169e896ea6520a8daebee68e5844e605526c4
-- 
2.36.0





^ permalink raw reply	[flat|nested] 20+ messages in thread

* bug#55335: [PATCH Shepherd 1/3] service: 'make-inetd-constructor' accepts a list of endpoints.
  2022-05-18 14:06     ` bug#55335: [PATCH Shepherd 0/3] Endpoints for inetd services + IPv6-only endpoints Ludovic Courtès
@ 2022-05-18 14:06       ` Ludovic Courtès
  2022-05-18 14:06       ` bug#55335: [PATCH Shepherd 2/3] tests: Update inetd tests to pass " Ludovic Courtès
  2022-05-18 14:06       ` bug#55335: [PATCH Shepherd 3/3] Interpret AF_INET6 endpoints as IPv6-only Ludovic Courtès
  2 siblings, 0 replies; 20+ messages in thread
From: Ludovic Courtès @ 2022-05-18 14:06 UTC (permalink / raw)
  To: 55335; +Cc: Ludovic Courtès

* modules/shepherd/service.scm (endpoint->listening-socket)
(open-sockets): New procedures.
(make-inetd-constructor): Change 'address' parameter to 'endpoints'.
Mark #:socket-style, #:socket-owner, #:socket-group, #:socket-directory-permissions,
and #:listen-backlog as deprecated.
[spawn-child-service, accept-clients]: Take 'server-address' parameter
and use it.  Update callers.
Add compatibility later for when ENDPOINTS is an address.
(make-inetd-destructor): Adjust.
(make-systemd-destructor)[endpoint->listening-socket, open-sockets]:
Remove.
Adjust to new return value of 'open-sockets'.
* NEWS: Mention it.
---
 NEWS                         |  13 ++
 doc/shepherd.texi            |  54 ++++----
 modules/shepherd/service.scm | 255 +++++++++++++++++------------------
 3 files changed, 161 insertions(+), 161 deletions(-)

diff --git a/NEWS b/NEWS
index c51e8e2..4ce7a48 100644
--- a/NEWS
+++ b/NEWS
@@ -12,6 +12,19 @@ Copyright © 2013-2014, 2016, 2018-2020, 2022 Ludovic Courtès <ludo@gnu.org>
 Please send Shepherd bug reports to bug-guix@gnu.org.
 
 * Changes in version 0.9.1
+** ‘make-inetd-constructor’ now accepts a list of endpoints
+
+In 0.9.0, ‘make-inetd-constructor’ would take a single address as returned by
+‘make-socket-address’.  This was insufficiently flexible since it didn’t let
+you have an inetd service with multiple endpoints.  ‘make-inetd-constructor’
+now takes a list of endpoints, similar to what ‘make-systemd-constructor’
+already did.
+
+For compatibility with 0.9.0, if the second argument to
+‘make-systemd-constructor’ is an address, it is automatically converted to a
+list of endpoints.  This behavior will be preserved for at least the whole
+0.9.x series.
+
 ** ‘shepherd’ reports whether a service is transient
 ** ‘herd status’ shows whether a service is transient
 ** Fix possible file descriptor leak in ‘make-inetd-constructor’
diff --git a/doc/shepherd.texi b/doc/shepherd.texi
index 3d01186..9efc48e 100644
--- a/doc/shepherd.texi
+++ b/doc/shepherd.texi
@@ -1082,11 +1082,28 @@ services, specifically those in @code{nowait} mode where the daemon is
 passed the newly-accepted socket connection while @command{shepherd} is
 in charge of listening.
 
-@deffn {procedure} make-inetd-constructor @var{command} @var{address}
-  [#:service-name-stem _] [#:requirements '()] @
-  [#:socket-style SOCK_STREAM] [#:listen-backlog 10] @
+Listening endpoints for such services are described as records built
+using the @code{endpoint} procedure:
+
+@deffn {procedure} endpoint @var{address} [#:name "unknown"] @
+  [#:style SOCK_STREAM] [#:backlog 128] @
   [#:socket-owner (getuid)] [#:socket-group (getgid)] @
-  [#:socket-directory-permissions #o755] @
+  [#:socket-directory-permissions #o755]
+Return a new endpoint called @var{name} of @var{address}, an address as
+return by @code{make-socket-address}, with the given @var{style} and
+@var{backlog}.
+
+When @var{address} is of type @code{AF_UNIX}, @var{socket-owner} and
+@var{socket-group} are strings or integers that specify its ownership and that
+of its parent directory; @var{socket-directory-permissions} specifies the
+permissions for its parent directory.
+@end deffn
+
+The inetd service constructor takes a command and a list of such
+endpoints:
+
+@deffn {procedure} make-inetd-constructor @var{command} @var{endpoints}
+  [#:service-name-stem _] [#:requirements '()] @
   [#:max-connections (default-inetd-max-connections)] @
   [#:user #f] @
   [#:group #f] @
@@ -1095,14 +1112,9 @@ in charge of listening.
   [#:file-creation-mask #f] [#:create-session? #t] @
   [#:resource-limits '()] @
   [#:environment-variables (default-environment-variables)]
-Return a procedure that opens a socket listening to @var{address}, an
-object as returned by @code{make-socket-address}, and accepting connections in
-the background; the @var{listen-backlog} argument is passed to @var{accept}.
-
-When @var{address} is of type @code{AF_UNIX}, @var{socket-owner} and
-@var{socket-group} are strings or integers that specify its ownership and that
-of its parent directory; @var{socket-directory-permissions} specifies the
-permissions for its parent directory.
+Return a procedure that opens sockets listening to @var{endpoints}, a list
+of objects as returned by @code{endpoint}, and accepting connections in the
+background.
 
 Upon a client connection, a transient service running @var{command} is
 spawned.  Only up to @var{max-connections} simultaneous connections are
@@ -1133,24 +1145,6 @@ environment (see below), which usually checks them using the libsystemd
 or libelogind
 @uref{https://www.freedesktop.org/software/systemd/man/sd_listen_fds.html,
 client library helper functions}.
-
-Listening endpoints for such services are described as records built
-using the @code{endpoint} procedure:
-
-@deffn {procedure} endpoint @var{address} [#:name "unknown"] @
-  [#:style SOCK_STREAM] [#:backlog 128] @
-  [#:socket-owner (getuid)] [#:socket-group (getgid)] @
-  [#:socket-directory-permissions #o755]
-Return a new endpoint called @var{name} of @var{address}, an address as
-return by @code{make-socket-address}, with the given @var{style} and
-@var{backlog}.
-
-When @var{address} is of type @code{AF_UNIX}, @var{socket-owner} and
-@var{socket-group} are strings or integers that specify its ownership and that
-of its parent directory; @var{socket-directory-permissions} specifies the
-permissions for its parent directory.
-@end deffn
-
 The constructor and destructor for systemd-style daemons are described
 below.
 
diff --git a/modules/shepherd/service.scm b/modules/shepherd/service.scm
index ded8283..e93466a 100644
--- a/modules/shepherd/service.scm
+++ b/modules/shepherd/service.scm
@@ -1225,6 +1225,90 @@ as argument, where SIGNAL defaults to `SIGTERM'."
   (lambda (ignored . args)
     (not (zero? (status:exit-val (system (apply string-append command)))))))
 
+\f
+;;;
+;;; Server endpoints.
+;;;
+
+;; Endpoint of a systemd-style or inetd-style service.
+(define-record-type <endpoint>
+  (make-endpoint name address style backlog owner group permissions)
+  endpoint?
+  (name        endpoint-name)                          ;string
+  (address     endpoint-address)                       ;socket address
+  (style       endpoint-style)                         ;SOCK_STREAM, etc.
+  (backlog     endpoint-backlog)                       ;integer
+  (owner       endpoint-socket-owner)                  ;integer
+  (group       endpoint-socket-group)                  ;integer
+  (permissions endpoint-socket-directory-permissions)) ;integer
+
+(define* (endpoint address
+                   #:key (name "unknown") (style SOCK_STREAM)
+                   (backlog 128)
+                   (socket-owner (getuid)) (socket-group (getgid))
+                   (socket-directory-permissions #o755))
+  "Return a new endpoint called @var{name} of @var{address}, an address as
+return by @code{make-socket-address}, with the given @var{style} and
+@var{backlog}.
+
+When @var{address} is of type @code{AF_UNIX}, @var{socket-owner} and
+@var{socket-group} are strings or integers that specify its ownership and that
+of its parent directory; @var{socket-directory-permissions} specifies the
+permissions for its parent directory."
+  (make-endpoint name address style backlog
+                 socket-owner socket-group
+                 socket-directory-permissions))
+
+(define (endpoint->listening-socket endpoint)
+  "Return a listening socket for ENDPOINT."
+  (match endpoint
+    (($ <endpoint> name address style backlog
+                   owner group permissions)
+     (let* ((sock    (non-blocking-port
+                      (socket (sockaddr:fam address) style 0)))
+            (owner   (if (integer? owner)
+                         owner
+                         (passwd:uid (getpwnam owner))))
+            (group   (if (integer? group)
+                         group
+                         (group:gid (getgrnam group)))))
+       (setsockopt sock SOL_SOCKET SO_REUSEADDR 1)
+       (when (= AF_UNIX (sockaddr:fam address))
+         (mkdir-p (dirname (sockaddr:path address)) permissions)
+         (chown (dirname (sockaddr:path address)) owner group)
+         (catch-system-error (delete-file (sockaddr:path address))))
+
+       (bind sock address)
+       (listen sock backlog)
+
+       (when (= AF_UNIX (sockaddr:fam address))
+         (chown sock owner group)
+         (chmod sock #o666))
+
+       sock))))
+
+(define (open-sockets endpoints)
+  "Return a list of listening sockets corresponding to ENDPOINTS, in the same
+order as ENDPOINTS.  If opening of binding one of them fails, an exception is
+thrown an previously-opened sockets are closed."
+  (let loop ((endpoints endpoints)
+             (result   '()))
+    (match endpoints
+      (()
+       (reverse result))
+      ((head tail ...)
+       (let ((sock (catch 'system-error
+                     (lambda ()
+                       (endpoint->listening-socket head))
+                     (lambda args
+                       ;; When opening one socket fails, abort the whole
+                       ;; process.
+                       (for-each (match-lambda
+                                   ((_ . socket) (close-port socket)))
+                                 result)
+                       (apply throw args)))))
+         (loop tail (cons sock result)))))))
+
 \f
 ;;;
 ;;; Inetd-style services.
@@ -1311,18 +1395,13 @@ as argument, where SIGNAL defaults to `SIGTERM'."
   ;; service.
   (make-parameter 100))
 
-(define* (make-inetd-constructor command address
+(define* (make-inetd-constructor command endpoints
                                  #:key
                                  (service-name-stem
                                   (match command
                                     ((program . _)
                                      (basename program))))
                                  (requirements '())
-                                 (socket-style SOCK_STREAM)
-                                 (socket-owner (getuid))
-                                 (socket-group (getgid))
-                                 (socket-directory-permissions #o755)
-                                 (listen-backlog 10)
                                  (max-connections
                                   (default-inetd-max-connections))
                                  (user #f)
@@ -1333,15 +1412,17 @@ as argument, where SIGNAL defaults to `SIGTERM'."
                                  (create-session? #t)
                                  (environment-variables
                                   (default-environment-variables))
-                                 (resource-limits '()))
-  "Return a procedure that opens a socket listening to @var{address}, an
-object as returned by @code{make-socket-address}, and accepting connections in
-the background; the @var{listen-backlog} argument is passed to @var{accept}.
+                                 (resource-limits '())
 
-When @var{address} is of type @code{AF_UNIX}, @var{socket-owner} and
-@var{socket-group} are strings or integers that specify its ownership and that
-of its parent directory; @var{socket-directory-permissions} specifies the
-permissions for its parent directory.
+                                 ;; Deprecated.
+                                 (socket-style SOCK_STREAM)
+                                 (socket-owner (getuid))
+                                 (socket-group (getgid))
+                                 (socket-directory-permissions #o755)
+                                 (listen-backlog 10))
+  "Return a procedure that opens sockets listening to @var{endpoints}, a list
+of objects as returned by @code{endpoint}, and accepting connections in the
+background.
 
 Upon a client connection, a transient service running @var{command} is
 spawned.  Only up to @var{max-connections} simultaneous connections are
@@ -1370,7 +1451,7 @@ The remaining arguments are as for @code{make-forkexec-constructor}."
                   connection-count (canonical-name service))
     (default-service-termination-handler service status))
 
-  (define (spawn-child-service connection client-address)
+  (define (spawn-child-service connection server-address client-address)
     (let* ((name    (child-service-name))
            (service (make <service>
                       #:provides (list name)
@@ -1387,7 +1468,7 @@ The remaining arguments are as for @code{make-forkexec-constructor}."
                                #:file-creation-mask file-creation-mask
                                #:create-session? create-session?
                                #:environment-variables
-                               (append (inetd-variables address
+                               (append (inetd-variables server-address
                                                         client-address)
                                    environment-variables)
                                #:resource-limits resource-limits)
@@ -1396,7 +1477,7 @@ The remaining arguments are as for @code{make-forkexec-constructor}."
       (register-services service)
       (start service)))
 
-  (define (accept-clients sock)
+  (define (accept-clients server-address sock)
     ;; Return a thunk that accepts client connections from SOCK.
     (lambda ()
       (let loop ()
@@ -1407,7 +1488,7 @@ The remaining arguments are as for @code{make-forkexec-constructor}."
                  (local-output
                   (l10n "Maximum number of ~a clients reached; \
 rejecting connection from ~:[~a~;~*local process~].")
-                  (socket-address->string address)
+                  (socket-address->string server-address)
                   (= AF_UNIX (sockaddr:fam client-address))
                   (socket-address->string client-address))
                  (close-port connection))
@@ -1415,46 +1496,35 @@ rejecting connection from ~:[~a~;~*local process~].")
                  (set! connection-count (+ 1 connection-count))
                  (local-output
                   (l10n "Accepted connection on ~a from ~:[~a~;~*local process~].")
-                  (socket-address->string address)
+                  (socket-address->string server-address)
                   (= AF_UNIX (sockaddr:fam client-address))
                   (socket-address->string client-address))
-                 (spawn-child-service connection client-address)))))
+                 (spawn-child-service connection
+                                      server-address client-address)))))
         (loop))))
 
   (lambda args
-    (let ((owner (if (integer? socket-owner)
-                     socket-owner
-                     (passwd:uid (getpwnam socket-owner))))
-          (group (if (integer? socket-group)
-                     socket-group
-                     (group:gid (getgrnam socket-group))))
-          (sock  (socket (sockaddr:fam address) socket-style 0)))
-      (catch #t
-        (lambda ()
-          (non-blocking-port sock)
-          (setsockopt sock SOL_SOCKET SO_REUSEADDR 1)
-
-          (when (= AF_UNIX (sockaddr:fam address))
-            (mkdir-p (dirname (sockaddr:path address))
-                     socket-directory-permissions)
-            (chown (dirname (sockaddr:path address)) owner group)
-            (catch-system-error (delete-file (sockaddr:path address))))
-          (bind sock address)
-          (when (= AF_UNIX (sockaddr:fam address))
-            (chown sock owner group)
-            (chmod sock #o666))
-
-          (listen sock listen-backlog)
-          (spawn-fiber (accept-clients sock))
-          sock)
-        (lambda args
-          (close-port sock)
-          (apply throw args))))))
+    (let* ((endpoints (match endpoints
+                        (((? endpoint?) ...) endpoints)
+                        (address (list (endpoint address
+                                                 #:style socket-style
+                                                 #:backlog listen-backlog
+                                                 #:socket-owner socket-owner
+                                                 #:socket-group socket-group
+                                                 #:socket-directory-permissions
+                                                 socket-directory-permissions)))))
+           (sockets   (open-sockets endpoints)))
+      (for-each (lambda (endpoint socket)
+                  (spawn-fiber
+                   (accept-clients (endpoint-address endpoint)
+                                   socket)))
+                endpoints sockets)
+      sockets)))
 
 (define (make-inetd-destructor)
   "Return a procedure that terminates an inetd service."
-  (lambda (sock)
-    (close-port sock)
+  (lambda (sockets)
+    (for-each close-port sockets)
     #f))
 
 \f
@@ -1462,35 +1532,6 @@ rejecting connection from ~:[~a~;~*local process~].")
 ;;; systemd-style services.
 ;;;
 
-;; Endpoint of a systemd-style service.
-(define-record-type <endpoint>
-  (make-endpoint name address style backlog owner group permissions)
-  endpoint?
-  (name        endpoint-name)                          ;string
-  (address     endpoint-address)                       ;socket address
-  (style       endpoint-style)                         ;SOCK_STREAM, etc.
-  (backlog     endpoint-backlog)                       ;integer
-  (owner       endpoint-socket-owner)                  ;integer
-  (group       endpoint-socket-group)                  ;integer
-  (permissions endpoint-socket-directory-permissions)) ;integer
-
-(define* (endpoint address
-                   #:key (name "unknown") (style SOCK_STREAM)
-                   (backlog 128)
-                   (socket-owner (getuid)) (socket-group (getgid))
-                   (socket-directory-permissions #o755))
-  "Return a new endpoint called @var{name} of @var{address}, an address as
-return by @code{make-socket-address}, with the given @var{style} and
-@var{backlog}.
-
-When @var{address} is of type @code{AF_UNIX}, @var{socket-owner} and
-@var{socket-group} are strings or integers that specify its ownership and that
-of its parent directory; @var{socket-directory-permissions} specifies the
-permissions for its parent directory."
-  (make-endpoint name address style backlog
-                 socket-owner socket-group
-                 socket-directory-permissions))
-
 (define (wait-for-readable ports)
   "Suspend the current task until one of @var{ports} is available for
 reading."
@@ -1538,58 +1579,10 @@ The colon-separated list of endpoint names.
 
 This must be paired with @code{make-systemd-destructor}."
   (lambda args
-    (define (endpoint->listening-socket endpoint)
-      ;; Return a listening socket for ENDPOINT.
-      (match endpoint
-        (($ <endpoint> name address style backlog
-                       owner group permissions)
-         (let* ((sock    (non-blocking-port
-                          (socket (sockaddr:fam address) style 0)))
-                (owner   (if (integer? owner)
-                             owner
-                             (passwd:uid (getpwnam owner))))
-                (group   (if (integer? group)
-                             group
-                             (group:gid (getgrnam group)))))
-           (setsockopt sock SOL_SOCKET SO_REUSEADDR 1)
-           (when (= AF_UNIX (sockaddr:fam address))
-             (mkdir-p (dirname (sockaddr:path address)) permissions)
-             (chown (dirname (sockaddr:path address)) owner group)
-             (catch-system-error (delete-file (sockaddr:path address))))
-
-           (bind sock address)
-           (listen sock backlog)
-
-           (when (= AF_UNIX (sockaddr:fam address))
-             (chown sock owner group)
-             (chmod sock #o666))
-
-           sock))))
-
-    (define (open-sockets addresses)
-      (let loop ((endpoints endpoints)
-                 (result   '()))
-        (match endpoints
-          (()
-           (reverse result))
-          ((head tail ...)
-           (let ((sock (catch 'system-error
-                         (lambda ()
-                           (endpoint->listening-socket head))
-                         (lambda args
-                           ;; When opening one socket fails, abort the whole
-                           ;; process.
-                           (for-each (match-lambda
-                                       ((_ . socket) (close-port socket)))
-                                     result)
-                           (apply throw args)))))
-             (loop tail
-                   `((,(endpoint-name head) . ,sock) ,@result)))))))
-
-    (let* ((sockets   (open-sockets endpoints))
-           (ports     (match sockets
-                        (((names . ports) ...)
-                         ports)))
+    (let* ((ports     (open-sockets endpoints))
+           (sockets   (map (lambda (endpoint socket)
+                             (cons (endpoint-name endpoint) socket))
+                           endpoints ports))
            (variables (list (string-append "LISTEN_FDS="
                                            (number->string (length sockets)))
                             (string-append "LISTEN_FDNAMES="
-- 
2.36.0





^ permalink raw reply related	[flat|nested] 20+ messages in thread

* bug#55335: [PATCH Shepherd 2/3] tests: Update inetd tests to pass a list of endpoints.
  2022-05-18 14:06     ` bug#55335: [PATCH Shepherd 0/3] Endpoints for inetd services + IPv6-only endpoints Ludovic Courtès
  2022-05-18 14:06       ` bug#55335: [PATCH Shepherd 1/3] service: 'make-inetd-constructor' accepts a list of endpoints Ludovic Courtès
@ 2022-05-18 14:06       ` Ludovic Courtès
  2022-05-18 14:06       ` bug#55335: [PATCH Shepherd 3/3] Interpret AF_INET6 endpoints as IPv6-only Ludovic Courtès
  2 siblings, 0 replies; 20+ messages in thread
From: Ludovic Courtès @ 2022-05-18 14:06 UTC (permalink / raw)
  To: 55335; +Cc: Ludovic Courtès

* tests/inetd.sh: Pass 'make-inetd-constructor' a list of endpoints.
---
 tests/inetd.sh | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/tests/inetd.sh b/tests/inetd.sh
index ef18800..83037bf 100644
--- a/tests/inetd.sh
+++ b/tests/inetd.sh
@@ -42,15 +42,18 @@ cat > "$conf" <<EOF
  (make <service>
    #:provides '(test-inetd)
    #:start (make-inetd-constructor %command
-                                   (make-socket-address AF_INET
-                                                        INADDR_LOOPBACK
-                                                        $PORT))
+                                   (list
+                                    (endpoint (make-socket-address
+                                               AF_INET
+                                               INADDR_LOOPBACK
+                                               $PORT))))
    #:stop  (make-inetd-destructor))
  (make <service>
    #:provides '(test-inetd-unix)
    #:start (make-inetd-constructor %command
-                                   (make-socket-address AF_UNIX
-                                                        "$service_socket")
+                                   (list
+                                    (endpoint (make-socket-address
+                                               AF_UNIX "$service_socket")))
                                    #:max-connections 5)
    #:stop  (make-inetd-destructor)))
 
-- 
2.36.0





^ permalink raw reply related	[flat|nested] 20+ messages in thread

* bug#55335: [PATCH Shepherd 3/3] Interpret AF_INET6 endpoints as IPv6-only.
  2022-05-18 14:06     ` bug#55335: [PATCH Shepherd 0/3] Endpoints for inetd services + IPv6-only endpoints Ludovic Courtès
  2022-05-18 14:06       ` bug#55335: [PATCH Shepherd 1/3] service: 'make-inetd-constructor' accepts a list of endpoints Ludovic Courtès
  2022-05-18 14:06       ` bug#55335: [PATCH Shepherd 2/3] tests: Update inetd tests to pass " Ludovic Courtès
@ 2022-05-18 14:06       ` Ludovic Courtès
  2022-05-18 14:28         ` bug#55335: openssh-service no longer listens on IPv6 Ludovic Courtès
  2 siblings, 1 reply; 20+ messages in thread
From: Ludovic Courtès @ 2022-05-18 14:06 UTC (permalink / raw)
  To: 55335; +Cc: Ludovic Courtès

* configure.ac: Check the values of IPPROTO_IPV6 and IPV6_V6ONLY.
* modules/shepherd/system.scm.in (ipv6-only): New procedure.
* modules/shepherd/service.scm (endpoint->listening-socket): Call it if
ADDRESS is AF_INET6.
(define-as-needed): New macro.
(IN6ADDR_LOOPBACK, IN6ADDR_ANY): New variables.
* tests/inetd.sh: Add 'test-inetd6' and 'test-inetd-v6-only' services.
Test them.
---
 NEWS                           | 11 +++++++
 configure.ac                   | 12 +++++++
 doc/shepherd.texi              | 14 ++++++++
 modules/shepherd/service.scm   | 19 +++++++++++
 modules/shepherd/system.scm.in | 11 +++++++
 tests/inetd.sh                 | 58 ++++++++++++++++++++++++++++++++++
 6 files changed, 125 insertions(+)

diff --git a/NEWS b/NEWS
index 4ce7a48..3798b31 100644
--- a/NEWS
+++ b/NEWS
@@ -25,6 +25,17 @@ For compatibility with 0.9.0, if the second argument to
 list of endpoints.  This behavior will be preserved for at least the whole
 0.9.x series.
 
+** ‘AF_INET6’ endpoints are now interpreted as IPv6-only
+
+In 0.9.0, using an ‘AF_INET6’ endpoint for ‘make-systemd-constructor’ would
+usually have the effect of making the service available on both IPv6 and IPv4.
+This is due to the default behavior of Linux, which is to bind IPv6 addresses
+as IPv4 as well (the default behavior can be changed by running
+‘sysctl net.ipv6.bindv6only 1’).
+
+‘AF_INET6’ endpoints are now interpreted as IPv6-only.  Thus, if a service is
+to be made available both as IPv6 and IPv4, two endpoints must be used.
+
 ** ‘shepherd’ reports whether a service is transient
 ** ‘herd status’ shows whether a service is transient
 ** Fix possible file descriptor leak in ‘make-inetd-constructor’
diff --git a/configure.ac b/configure.ac
index bf91560..b745813 100644
--- a/configure.ac
+++ b/configure.ac
@@ -141,6 +141,18 @@ AC_SUBST([SIG_BLOCK])
 AC_SUBST([SIG_UNBLOCK])
 AC_SUBST([SIG_SETMASK])
 
+dnl Check for constants not exported by Guile as of 3.0.8.
+AC_MSG_CHECKING([<netinet/in.h> constants])
+AC_COMPUTE_INT([IPPROTO_IPV6], [IPPROTO_IPV6], [
+  #include <sys/socket.h>
+  #include <netinet/in.h>])
+AC_COMPUTE_INT([IPV6_V6ONLY], [IPV6_V6ONLY], [
+  #include <sys/socket.h>
+  #include <netinet/in.h>])
+AC_MSG_RESULT([done])
+AC_SUBST([IPPROTO_IPV6])
+AC_SUBST([IPV6_V6ONLY])
+
 AC_MSG_CHECKING([whether to build crash handler])
 case "$host_os" in
   linux-gnu*)  build_crash_handler=yes;;
diff --git a/doc/shepherd.texi b/doc/shepherd.texi
index 9efc48e..841b854 100644
--- a/doc/shepherd.texi
+++ b/doc/shepherd.texi
@@ -1093,6 +1093,20 @@ Return a new endpoint called @var{name} of @var{address}, an address as
 return by @code{make-socket-address}, with the given @var{style} and
 @var{backlog}.
 
+When @var{address} is of type @code{AF_INET6}, the endpoint is
+@emph{IPv6-only}.  Thus, if you want a service available both on IPv4
+and IPv6, you need two endpoints.  For example, below is a list of
+endpoints to listen on port 4444 on all the network interfaces, both in
+IPv4 and IPv6 (``0.0.0.0'' for IPv4 and ``::0'' for IPv6):
+
+@lisp
+(list (endpoint (make-socket-address AF_INET INADDR_ANY 4444))
+      (endpoint (make-socket-address AF_INET6 IN6ADDR_ANY 4444)))
+@end lisp
+
+This is the list you would pass to @code{make-inetd-constructor} or
+@code{make-systemd-constructor}---see below.
+
 When @var{address} is of type @code{AF_UNIX}, @var{socket-owner} and
 @var{socket-group} are strings or integers that specify its ownership and that
 of its parent directory; @var{socket-directory-permissions} specifies the
diff --git a/modules/shepherd/service.scm b/modules/shepherd/service.scm
index e93466a..6df550c 100644
--- a/modules/shepherd/service.scm
+++ b/modules/shepherd/service.scm
@@ -1251,6 +1251,10 @@ as argument, where SIGNAL defaults to `SIGTERM'."
 return by @code{make-socket-address}, with the given @var{style} and
 @var{backlog}.
 
+When @var{address} is of type @code{AF_INET6}, the endpoint is
+@emph{IPv6-only}.  Thus, if you want a service available both on IPv4 and
+IPv6, you need two endpoints.
+
 When @var{address} is of type @code{AF_UNIX}, @var{socket-owner} and
 @var{socket-group} are strings or integers that specify its ownership and that
 of its parent directory; @var{socket-directory-permissions} specifies the
@@ -1273,6 +1277,11 @@ permissions for its parent directory."
                          group
                          (group:gid (getgrnam group)))))
        (setsockopt sock SOL_SOCKET SO_REUSEADDR 1)
+       (when (= AF_INET6 (sockaddr:fam address))
+         ;; Interpret AF_INET6 endpoints as IPv6-only.  This is contrary to
+         ;; the Linux defaults where listening on an IPv6 address also listens
+         ;; on its IPv4 counterpart.
+         (ipv6-only sock))
        (when (= AF_UNIX (sockaddr:fam address))
          (mkdir-p (dirname (sockaddr:path address)) permissions)
          (chown (dirname (sockaddr:path address)) owner group)
@@ -1309,6 +1318,16 @@ thrown an previously-opened sockets are closed."
                        (apply throw args)))))
          (loop tail (cons sock result)))))))
 
+(define-syntax-rule (define-as-needed name value)
+  (unless (defined? 'name)
+    (module-define! (current-module) 'name value)
+    (module-export! (current-module) '(name))))
+
+;; These values are not defined as of Guile 3.0.8.  Provide them as a
+;; convenience.
+(define-as-needed IN6ADDR_LOOPBACK 1)
+(define-as-needed IN6ADDR_ANY 0)
+
 \f
 ;;;
 ;;; Inetd-style services.
diff --git a/modules/shepherd/system.scm.in b/modules/shepherd/system.scm.in
index 2562764..0978c18 100644
--- a/modules/shepherd/system.scm.in
+++ b/modules/shepherd/system.scm.in
@@ -32,6 +32,7 @@
             prctl
             PR_SET_CHILD_SUBREAPER
             getpgid
+            ipv6-only
             SFD_CLOEXEC
             signalfd
             consume-signalfd-siginfo
@@ -141,6 +142,16 @@ ctrlaltdel(8) and see kernel/reboot.c in Linux."
                    (list err))
             result)))))
 
+(define (ipv6-only port)
+  "Make PORT, a file port backed by a socket, IPv6-only (using the IPV6_V6ONLY
+socket option) and return PORT.
+
+This is useful when willing to make a listening socket that operates on IPv6
+only (by default, Linux binds AF_INET6 addresses on IPv4 as well)."
+  ;; As of Guile 3.0.8, IPPROTO_IPV6 and IPV6_V6ONLY are not exported.
+  (setsockopt port @IPPROTO_IPV6@ @IPV6_V6ONLY@ 1)
+  port)
+
 (define (allocate-sigset)
   (bytevector->pointer (make-bytevector @SIZEOF_SIGSET_T@)))
 
diff --git a/tests/inetd.sh b/tests/inetd.sh
index 83037bf..c05d6fe 100644
--- a/tests/inetd.sh
+++ b/tests/inetd.sh
@@ -48,6 +48,28 @@ cat > "$conf" <<EOF
                                                INADDR_LOOPBACK
                                                $PORT))))
    #:stop  (make-inetd-destructor))
+ (make <service>
+   #:provides '(test-inetd6)
+   #:start (make-inetd-constructor %command
+                                   (list
+                                    (endpoint (make-socket-address
+                                               AF_INET
+                                               INADDR_LOOPBACK
+                                               $PORT))
+                                    (endpoint (make-socket-address
+                                               AF_INET6
+                                               IN6ADDR_LOOPBACK
+                                               $PORT))))
+   #:stop  (make-inetd-destructor))
+ (make <service>
+   #:provides '(test-inetd-v6-only)
+   #:start (make-inetd-constructor %command
+                                   (list
+                                    (endpoint (make-socket-address
+                                               AF_INET6
+                                               IN6ADDR_LOOPBACK
+                                               $PORT))))
+   #:stop  (make-inetd-destructor))
  (make <service>
    #:provides '(test-inetd-unix)
    #:start (make-inetd-constructor %command
@@ -81,6 +103,7 @@ test $($herd status | grep '\+' | wc -l) -eq 2
 converse_with_echo_server ()
 {
     guile -c "(use-modules (ice-9 match) (ice-9 rdelim))
+      (define IN6ADDR_LOOPBACK 1)
       (define address $1)
       (define sock (socket (sockaddr:fam address) SOCK_STREAM 0))
       (connect sock address)
@@ -98,10 +121,45 @@ do
 	"(make-socket-address AF_INET INADDR_LOOPBACK $PORT)"
 done
 
+# Unavailable on IPv6.
+! converse_with_echo_server \
+    "(make-socket-address AF_INET6 IN6ADDR_LOOPBACK $PORT)"
+
 $herd stop test-inetd
 ! converse_with_echo_server \
   "(make-socket-address AF_INET INADDR_LOOPBACK $PORT)"
 
+if guile -c '(socket AF_INET6 SOCK_STREAM 0)'; then
+    # Test IPv6 support.
+    $herd start test-inetd6
+
+    converse_with_echo_server \
+	"(make-socket-address AF_INET6 IN6ADDR_LOOPBACK $PORT)"
+    converse_with_echo_server \
+	"(make-socket-address AF_INET INADDR_LOOPBACK $PORT)"
+
+    $herd stop test-inetd6
+
+    ! converse_with_echo_server \
+	"(make-socket-address AF_INET6 IN6ADDR_LOOPBACK $PORT)"
+    ! converse_with_echo_server \
+	"(make-socket-address AF_INET INADDR_LOOPBACK $PORT)"
+
+    $herd start test-inetd-v6-only
+
+    converse_with_echo_server \
+	"(make-socket-address AF_INET6 IN6ADDR_LOOPBACK $PORT)"
+    ! converse_with_echo_server \
+	"(make-socket-address AF_INET INADDR_LOOPBACK $PORT)"
+
+    $herd stop test-inetd-v6-only
+
+    ! converse_with_echo_server \
+	"(make-socket-address AF_INET6 IN6ADDR_LOOPBACK $PORT)"
+    ! converse_with_echo_server \
+	"(make-socket-address AF_INET INADDR_LOOPBACK $PORT)"
+fi
+
 # Now test inetd on a Unix-domain socket.
 
 $herd start test-inetd-unix
-- 
2.36.0





^ permalink raw reply related	[flat|nested] 20+ messages in thread

* bug#55335: openssh-service no longer listens on IPv6
  2022-05-18 14:06       ` bug#55335: [PATCH Shepherd 3/3] Interpret AF_INET6 endpoints as IPv6-only Ludovic Courtès
@ 2022-05-18 14:28         ` Ludovic Courtès
  2022-05-22 20:08           ` Ludovic Courtès
  0 siblings, 1 reply; 20+ messages in thread
From: Ludovic Courtès @ 2022-05-18 14:28 UTC (permalink / raw)
  To: 55335

Ludovic Courtès <ludo@gnu.org> skribis:

> + (make <service>
> +   #:provides '(test-inetd6)
> +   #:start (make-inetd-constructor %command
> +                                   (list
> +                                    (endpoint (make-socket-address
> +                                               AF_INET
> +                                               INADDR_LOOPBACK
> +                                               $PORT))
> +                                    (endpoint (make-socket-address
> +                                               AF_INET6
> +                                               IN6ADDR_LOOPBACK
> +                                               $PORT))))
> +   #:stop  (make-inetd-destructor))
> + (make <service>
> +   #:provides '(test-inetd-v6-only)
> +   #:start (make-inetd-constructor %command
> +                                   (list
> +                                    (endpoint (make-socket-address
> +                                               AF_INET6
> +                                               IN6ADDR_LOOPBACK
> +                                               $PORT))))
> +   #:stop  (make-inetd-destructor))

I should point out that this new test hangs with Fibers 1.1.0; we need
this fix:

  https://github.com/wingo/fibers/commit/c25dcb9cc4b5b977474ffe555b40ce2f1d0d1edc

I’ve contacted Aleix to see if we could release Fibers 1.1.1.  Otherwise
we’ll use a snapshot in Guix.

Ludo’.




^ permalink raw reply	[flat|nested] 20+ messages in thread

* bug#55335: openssh-service no longer listens on IPv6
  2022-05-18 14:28         ` bug#55335: openssh-service no longer listens on IPv6 Ludovic Courtès
@ 2022-05-22 20:08           ` Ludovic Courtès
  2022-05-22 22:35             ` Jack Hill
  2022-05-23 15:29             ` Simon Streit
  0 siblings, 2 replies; 20+ messages in thread
From: Ludovic Courtès @ 2022-05-22 20:08 UTC (permalink / raw)
  To: 55335-done, Christopher Baines, Jack Hill

Hello!

With Shepherd 0.9.1 released, I believe Guix commit
d2b3400f79ffaed3357650307376ab69a7ec3b1b fixes this bug for good, also
adding a system test for SSH access over IPv6 (both with OpenSSH and
Dropbear).

Let me know if anything’s amiss!

Thanks,
Ludo’.




^ permalink raw reply	[flat|nested] 20+ messages in thread

* bug#55335: openssh-service no longer listens on IPv6
  2022-05-22 20:08           ` Ludovic Courtès
@ 2022-05-22 22:35             ` Jack Hill
  2022-05-23 13:30               ` Ludovic Courtès
  2022-05-23 15:29             ` Simon Streit
  1 sibling, 1 reply; 20+ messages in thread
From: Jack Hill @ 2022-05-22 22:35 UTC (permalink / raw)
  To: Ludovic Courtès; +Cc: Christopher Baines, 55335-done

[-- Attachment #1: Type: text/plain, Size: 584 bytes --]

On Sun, 22 May 2022, Ludovic Courtès wrote:

> Hello!
>
> With Shepherd 0.9.1 released, I believe Guix commit
> d2b3400f79ffaed3357650307376ab69a7ec3b1b fixes this bug for good, also
> adding a system test for SSH access over IPv6 (both with OpenSSH and
> Dropbear).
>
> Let me know if anything’s amiss!
>
> Thanks,
> Ludo’.

It's working well for me, allowing connections over both v4 and v6. I have 
another host that I can only access with a v6 via wireguard address, which 
I haven't been able to upgrade yet. I don't anticipate any problems there 
though.

Many thanks!
Jack

^ permalink raw reply	[flat|nested] 20+ messages in thread

* bug#55335: openssh-service no longer listens on IPv6
  2022-05-22 22:35             ` Jack Hill
@ 2022-05-23 13:30               ` Ludovic Courtès
  0 siblings, 0 replies; 20+ messages in thread
From: Ludovic Courtès @ 2022-05-23 13:30 UTC (permalink / raw)
  To: Jack Hill; +Cc: Christopher Baines, 55335-done

Hi Jack,

Jack Hill <jackhill@jackhill.us> skribis:

> It's working well for me, allowing connections over both v4 and v6. I
> have another host that I can only access with a v6 via wireguard
> address, which I haven't been able to upgrade yet. I don't anticipate
> any problems there though.

Good, thanks for reporting back!

Ludo’.




^ permalink raw reply	[flat|nested] 20+ messages in thread

* bug#55335: openssh-service no longer listens on IPv6
  2022-05-22 20:08           ` Ludovic Courtès
  2022-05-22 22:35             ` Jack Hill
@ 2022-05-23 15:29             ` Simon Streit
  1 sibling, 0 replies; 20+ messages in thread
From: Simon Streit @ 2022-05-23 15:29 UTC (permalink / raw)
  To: 55335; +Cc: ludo, mail

Ludovic Courtès <ludo@gnu.org> writes:

> Let me know if anything’s amiss!

Looking all good.  v4 and v6 connections are working now.





^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2022-05-23 15:31 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-05-09 10:39 bug#55335: openssh-service no longer listens on IPv6 Christopher Baines
2022-05-13 12:21 ` Christopher Baines
2022-05-13 14:23 ` bug#55335: [PATCH] services: Allow shepherd to listen for IPv6 connections to openssh Christopher Baines
2022-05-13 15:23   ` Jack Hill
2022-05-13 15:25     ` Jack Hill
2022-05-14  8:42     ` bug#55335: openssh-service no longer listens on IPv6 Ludovic Courtès
2022-05-14 14:16   ` Ludovic Courtès
2022-05-18 14:06     ` bug#55335: [PATCH Shepherd 0/3] Endpoints for inetd services + IPv6-only endpoints Ludovic Courtès
2022-05-18 14:06       ` bug#55335: [PATCH Shepherd 1/3] service: 'make-inetd-constructor' accepts a list of endpoints Ludovic Courtès
2022-05-18 14:06       ` bug#55335: [PATCH Shepherd 2/3] tests: Update inetd tests to pass " Ludovic Courtès
2022-05-18 14:06       ` bug#55335: [PATCH Shepherd 3/3] Interpret AF_INET6 endpoints as IPv6-only Ludovic Courtès
2022-05-18 14:28         ` bug#55335: openssh-service no longer listens on IPv6 Ludovic Courtès
2022-05-22 20:08           ` Ludovic Courtès
2022-05-22 22:35             ` Jack Hill
2022-05-23 13:30               ` Ludovic Courtès
2022-05-23 15:29             ` Simon Streit
2022-05-14 15:49   ` Ludovic Courtès
2022-05-14 19:09     ` Jack Hill
2022-05-17 21:33     ` Christopher Baines
2022-05-18  9:30       ` Ludovic Courtès

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/guix.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).