>Synopsis:      relayd's 'listen on' support is incomplete
>Category:      system
>Environment:
        System      : OpenBSD 6.6
        Details     : OpenBSD 6.6 (GENERIC.MP) #372: Sat Oct 12 10:56:27 MDT 
2019
                         
dera...@amd64.openbsd.org:/usr/src/sys/arch/amd64/compile/GENERIC.MP

        Architecture: OpenBSD.amd64
        Machine     : amd64

>Description:

        relayd's `listen on` syntax for relays is inconstent with httpd's.

        httpd.conf(8):

        >     listen on address [tls] port number
        >             Set the listen address and port.  This statement can be 
specified
        >             multiple times.

        relayd.conf(8):

        >     listen on address port port [tls]
        >             Specify the address and port for the relay to listen on.  
The
        >             relay will accept incoming connections to the specified 
address.
        >             If the tls keyword is present, the relay will accept 
connections
        >             using the encrypted TLS protocol.

        a. The location of the `tls` directive is different (but I've also 
suggested maybe
        that shouldn't even be there: 
https://marc.info/?l=openbsd-bugs&m=158140394525562)
        
        b. The address doesn't accept "*"

        c. The listen can be specified multiple times but this isn't mentioned 
in the manpage
        nor the example.

        d. Loading a tls listener fails with nonsense if it comes after any 
other listener.

>How-To-Repeat:

        a. Using `listen on .. tls` like httpd fails:

        ```
        # relayd.conf
        table <web> { "127.0.0.1" }

        http protocol web {
                # Return HTTP/HTML error pages to the client
                return error
        }

        relay https_proxy { 
                listen on 0.0.0.0 tls port 443

                protocol web
                forward to <web> port 8080
        }
        ```

        producing:

        ```
        $ doas pkill relayd ; doas relayd -d -v -f relayd.conf  
        doas (ko...@matriculate.lan) password: 
        startup
        relayd.conf:11: syntax error
        relayd.conf:13: protocol web defined twice
        no actions, nothing to do
        unused protocol: web
        pfe exiting, pid 14820
        ca exiting, pid 71629
        hce exiting, pid 79574
        ca exiting, pid 66728
        ca exiting, pid 14844
        relay exiting, pid 86318
        relay exiting, pid 53154
        relay exiting, pid 69497
        ```

        b. This variant using `listen on *` fails:

        ```
        # relayd.conf                                                           
                                              
        table <web> { "127.0.0.1" }

        http protocol web {
                # Return HTTP/HTML error pages to the client
                return error
        }

        relay https_proxy { 
                listen on * port 80

                protocol web
                forward to <web> port 8080
        }
        ```

        ```
        $ doas pkill relayd ; doas relayd -d -v -f relayd.conf  
        startup
        relayd.conf:11: syntax error
        relayd.conf:13: protocol web defined twice
        no actions, nothing to do
        unused protocol: web
        hce exiting, pid 15860
        ca exiting, pid 10897
        ca exiting, pid 478
        ca exiting, pid 84444
        pfe exiting, pid 92126
        relay exiting, pid 53380
        relay exiting, pid 23032
        relay exiting, pid 71637
        ```

        c. Manually expanding "*" to two listen directives like httpd(8) does 
works:

        ```
        # relayd.conf
        table <web> { "127.0.0.1" }

        http protocol web {
                # Return HTTP/HTML error pages to the client
                return error
        }

        relay https_proxy {
                listen on 0.0.0.0 port 80
                listen on :: port 80

                protocol web
                forward to <web> port 8080
        }
        ```

        producing IPv4/IPv6 dual stack listeners:

        ```
        $ doas pkill relayd ; doas relayd -d -v -f relayd.conf  
        startup
        adding 1 hosts from table web:8080 (no check)
        adding 1 hosts from table web:8080 (no check)
        adding 1 hosts from table web:8080 (no check)
        adding 1 hosts from table web:8080 (no check)
        adding 1 hosts from table web:8080 (no check)
        adding 1 hosts from table web:8080 (no check)
        ```

        ```
        $ fstat | grep relayd | grep internet | grep stream
        _relayd  relayd     29109   10* internet stream tcp 0x0 *:80
        _relayd  relayd     29109   11* internet6 stream tcp 0x0 *:80
        _relayd  relayd     64089   10* internet stream tcp 0x0 *:80
        _relayd  relayd     64089   11* internet6 stream tcp 0x0 *:80
        _relayd  relayd     51163   10* internet stream tcp 0x0 *:80
        _relayd  relayd     51163   11* internet6 stream tcp 0x0 *:80
        ```

        It would be extremely helpful in the future if this feature was 
mentioned in the
        manpage and example file.

        d. 

        Get a cert so we can use HTTPS:

        ```
        $ openssl req -x509 -newkey rsa:4096 -subj '/CN='"localhost" -nodes 
-keyout localhost.key -out localhost.pem -days 3
        $ doas cp localhost.pem /etc/ssl/localhost.crt
        $ doas cp localhost.key /etc/ssl/private/localhost.key
        ```

        This config works fine:

        ```
        table <web> { "127.0.0.1" }

        http protocol web {
                # Return HTTP/HTML error pages to the client
                return error

                tls keypair localhost
        }

        relay https_proxy { 
                listen on 0.0.0.0 port 443 tls
                #listen on :: port 443 tls
                listen on 0.0.0.0 port 80
                listen on :: port 80

                protocol web
                forward to <web> port 8080
        }
        ```

        producing working (IPv4,https), (IPv4,http), and (IPv6,http) sockets:

        ```
        $ curl -4 --cacert /etc/ssl/localhost.crt https://localhost 
        <!DOCTYPE html>
        <html>
        <head>
        <title>500 Internal Server Error</title>
        <style type="text/css"><!--
        body { background-color: #a00000; color: white; font-family: 'Comic 
Sans MS', 'Chalkboard SE', 'Comic Neue', sans-serif; }
        hr { border: 0; border-bottom: 1px dashed; }

        --></style>
        </head>
        <body>
        <h1>Internal Server Error</h1>
        <div id='m'></div>
        <div id='l'></div>
        <hr><address>OpenBSD relayd at 0.0.0.0 port 443</address>
        </body>
        </html>
        ```


        ```
        $ curl -4 http://localhost                                   
        <!DOCTYPE html>
        <html>
        <head>
        <title>500 Internal Server Error</title>
        <style type="text/css"><!--
        body { background-color: #a00000; color: white; font-family: 'Comic 
Sans MS', 'Chalkboard SE', 'Comic Neue', sans-serif; }
        hr { border: 0; border-bottom: 1px dashed; }

        --></style>
        </head>
        <body>
        <h1>Internal Server Error</h1>
        <div id='m'></div>
        <div id='l'></div>
        <hr><address>OpenBSD relayd at 0.0.0.0 port 80</address>
        </body>
        </html>
        ```


        ```
        $ curl -6 http://localhost 
        <!DOCTYPE html>
        <html>
        <head>
        <title>500 Internal Server Error</title>
        <style type="text/css"><!--
        body { background-color: #a00000; color: white; font-family: 'Comic 
Sans MS', 'Chalkboard SE', 'Comic Neue', sans-serif; }
        hr { border: 0; border-bottom: 1px dashed; }

        --></style>
        </head>
        <body>
        <h1>Internal Server Error</h1>
        <div id='m'></div>
        <div id='l'></div>
        <hr><address>OpenBSD relayd at :: port 80</address>
        </body>
        </html>
        ```



        But trying to enable the last combination fails:

        ```
        # relayd.conf
        table <web> { "127.0.0.1" }

        http protocol web {
                # Return HTTP/HTML error pages to the client
                return error

                tls keypair localhost
        }

        relay https_proxy {
                listen on 0.0.0.0 port 443 tls
                listen on :: port 443 tls
                listen on 0.0.0.0 port 80
                listen on :: port 80

                protocol web
                forward to <web> port 8080
        }
        ```

        ```
        $ doas pkill relayd ; doas relayd -d -v -f relayd.conf  
        startup
        relayd.conf:18: cannot load certificates for relay https_proxy2:443
        pfe exiting, pid 26880
        ca exiting, pid 15627
        ca exiting, pid 91004
        ca exiting, pid 26754
        hce exiting, pid 33589
        relay exiting, pid 14187
        relay exiting, pid 33426
        relay exiting, pid 21591
        ```

        This even happens with just

        ```
                listen on :: port 443 tls
                listen on 0.0.0.0 port 443 tls
        ```

        or

        ```
                listen on 0.0.0.0 port 443 tls
                listen on :: port 443 tls
        ```

        So it is *not* possible dual-stack a TLS relay.

>Fix:

        a. is a consistency issue.
        For problem b., workaround by manually expanding "*" into
          `listen on 0.0.0.0` and `listen on ::`.
        c. is a documentation issue.
        For problem d., workaround by splitting the listeners into duplicate 
relays:

        ```
        table <web> { "127.0.0.1" }

        http protocol web {
                # Return HTTP/HTML error pages to the client
                return error

                tls keypair localhost
        }

        relay https_proxy { 
                listen on 0.0.0.0 port 443 tls
                listen on 0.0.0.0 port 80

                protocol web
                forward to <web> port 8080
        }

        relay https_proxy6 {
                listen on :: port 443 tls
                listen on :: port 80

                protocol web
                forward to <web> port 8080
        }
        ```

Reply via email to