]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: quic: handle perm error on bind during runtime
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Tue, 3 Oct 2023 14:11:40 +0000 (16:11 +0200)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Tue, 3 Oct 2023 14:52:02 +0000 (16:52 +0200)
Improve EACCES permission errors encounterd when using QUIC connection
socket at runtime :

* First occurence of the error on the process will generate a log
  warning. This should prevent users from using a privileged port
  without mandatory access rights.

* Socket mode will automatically fallback to listener socket for the
  receiver instance. This requires to duplicate the settings from the
  bind_conf to the receiver instance to support configurations with
  multiple addresses on the same bind line.

include/haproxy/receiver-t.h
src/proto_quic.c
src/quic_conn.c
src/quic_sock.c

index f7c934726e39a5d68aec6490ff01b61c648a4ff4..e21f06b190d717e4b1e55022eb7a86d18ce2f15f 100644 (file)
@@ -28,6 +28,7 @@
 #include <haproxy/api-t.h>
 #include <haproxy/namespace-t.h>
 #include <haproxy/proto_reverse_connect-t.h>
+#include <haproxy/quic_sock-t.h>
 #include <haproxy/thread.h>
 
 /* Bit values for receiver->flags */
@@ -79,6 +80,7 @@ struct receiver {
        struct list proto_list;          /* list in the protocol header */
 #ifdef USE_QUIC
        struct mt_list rxbuf_list;       /* list of buffers to receive and dispatch QUIC datagrams. */
+       enum quic_sock_mode quic_mode;   /* QUIC socket allocation strategy */
 #endif
        struct {
                struct task *task;  /* Task used to open connection for reverse. */
index 22776cd83c38dae29ae9740cf664dcce444aaf14..73a48e44baf6130153089b413b6cbc1236f06cd3 100644 (file)
@@ -645,6 +645,11 @@ static int quic_bind_listener(struct listener *listener, char *errmsg, int errle
                goto udp_return;
        }
 
+       /* Duplicate quic_mode setting from bind_conf. Useful to overwrite it
+        * at runtime per receiver instance.
+        */
+       listener->rx.quic_mode = listener->bind_conf->quic_mode;
+
        /* Set IP_PKTINFO to retrieve destination address on recv. */
        fd = listener->rx.fd;
        switch (addr.ss_family) {
index a2d28c7b049358f2079171da44e4e244b2086930..f2957f4eda03718555739d28b6b4cf879b2ce102 100644 (file)
@@ -1264,7 +1264,7 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
 
        conn_id->qc = qc;
 
-       if (l->bind_conf->quic_mode == QUIC_SOCK_MODE_CONN &&
+       if (HA_ATOMIC_LOAD(&l->rx.quic_mode) == QUIC_SOCK_MODE_CONN &&
            (global.tune.options & GTUNE_QUIC_SOCK_PER_CONN) &&
            is_addr(local_addr)) {
                TRACE_USER("Allocate a socket for QUIC connection", QUIC_EV_CONN_INIT, qc);
index 1cbb71f9e27f5401c766988cab99616e62bb5923..3b3c3a7a28ed94d242b0cd1956980c7689aeea9f 100644 (file)
@@ -46,6 +46,9 @@
 
 #define TRACE_SOURCE    &trace_quic
 
+/* Log only first EACCES bind() error runtime occurence. */
+static volatile char quic_bind_eacces_warn = 0;
+
 /* Retrieve a connection's source address. Returns -1 on failure. */
 int quic_sock_get_src(struct connection *conn, struct sockaddr *addr, socklen_t len)
 {
@@ -812,7 +815,8 @@ int qc_rcv_buf(struct quic_conn *qc)
 void qc_alloc_fd(struct quic_conn *qc, const struct sockaddr_storage *src,
                  const struct sockaddr_storage *dst)
 {
-       struct proxy *p = qc->li->bind_conf->frontend;
+       struct bind_conf *bc = qc->li->bind_conf;
+       struct proxy *p = bc->frontend;
        int fd = -1;
        int ret;
 
@@ -854,8 +858,20 @@ void qc_alloc_fd(struct quic_conn *qc, const struct sockaddr_storage *src,
                goto err;
 
        ret = bind(fd, (struct sockaddr *)src, get_addr_len(src));
-       if (ret < 0)
+       if (ret < 0) {
+               if (errno == EACCES) {
+                       if (!quic_bind_eacces_warn) {
+                               send_log(p, LOG_WARNING,
+                                        "Permission error on QUIC socket binding for proxy %s. Consider using setcap cap_net_bind_service (Linux only) or running as root.\n",
+                                        p->id);
+                               quic_bind_eacces_warn = 1;
+                       }
+
+                       /* Fallback to listener socket for this receiver instance. */
+                       HA_ATOMIC_STORE(&qc->li->rx.quic_mode, QUIC_SOCK_MODE_LSTNR);
+               }
                goto err;
+       }
 
        ret = connect(fd, (struct sockaddr *)dst, get_addr_len(dst));
        if (ret < 0)