relayd 'listen on' inconsistencies

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

relayd 'listen on' inconsistencies

Nick Guenther-2
>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
                         [hidden email]:/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 ([hidden email]) 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
        }
        ```