]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: connection: Implement and extented PROXY Protocol V2
authorDavid S <11235david@gmail.com>
Fri, 9 May 2014 03:42:08 +0000 (23:42 -0400)
committerWilly Tarreau <w@1wt.eu>
Fri, 9 May 2014 06:25:38 +0000 (08:25 +0200)
This commit modifies the PROXY protocol V2 specification to support headers
longer than 255 bytes allowing for optional extensions.  It implements the
PROXY protocol V2 which is a binary representation of V1. This will make
parsing more efficient for clients who will know in advance exactly how
many bytes to read.  Also, it defines and implements some optional PROXY
protocol V2 extensions to send information about downstream SSL/TLS
connections.  Support for PROXY protocol V1 remains unchanged.

doc/configuration.txt
doc/proxy-protocol.txt
include/proto/connection.h
include/proto/ssl_sock.h
include/types/connection.h
include/types/server.h
src/backend.c
src/connection.c
src/server.c
src/ssl_sock.c
src/stream_interface.c

index 8207067a2ca5046b8812e00c490d55ae8019cabc..8cab0a274c09791a6846c962b8c9fd2182644155 100644 (file)
@@ -8808,6 +8808,42 @@ send-proxy
 
   Supported in default-server: No
 
+send-proxy-v2
+  The "send-proxy-v2" parameter enforces use of the PROXY protocol version 2
+  over any connection established to this server. The PROXY protocol informs
+  the other end about the layer 3/4 addresses of the incoming connection, so
+  that it can know the client's address or the public address it accessed to,
+  whatever the upper layer protocol. This setting must not be used if the
+  server isn't aware of this version of the protocol. See also the "send-proxy"
+  option of the "bind" keyword.
+
+  Supported in default-server: No
+
+send-proxy-v2-ssl
+  The "send-proxy-v2-ssl" parameter enforces use of the PROXY protocol version
+  2 over any connection established to this server. The PROXY protocol informs
+  the other end about the layer 3/4 addresses of the incoming connection, so
+  that it can know the client's address or the public address it accessed to,
+  whatever the upper layer protocol. In addition, the SSL information extension
+  of the PROXY protocol is added to the PROXY protocol header. This setting
+  must not be used if the server isn't aware of this version of the protocol.
+  See also the "send-proxy-v2" option of the "bind" keyword.
+
+  Supported in default-server: No
+
+send-proxy-v2-ssl-cn
+  The "send-proxy-v2-ssl" parameter enforces use of the PROXY protocol version
+  2 over any connection established to this server. The PROXY protocol informs
+  the other end about the layer 3/4 addresses of the incoming connection, so
+  that it can know the client's address or the public address it accessed to,
+  whatever the upper layer protocol. In addition, the SSL information extension
+  of the PROXY protocol, along along with the Common Name from the subject of
+  the client certificate (if any), is added to the PROXY protocol header. This
+  setting must not be used if the server isn't aware of this version of the
+  protocol. See also the "send-proxy-v2" option of the "bind" keyword.
+
+  Supported in default-server: No
+
 slowstart <start_time_in_ms>
   The "slowstart" parameter for a server accepts a value in milliseconds which
   indicates after how long a server which has just come back up will run at
index 96a459e3443d0d738fb2053e18a5d92c4a6478ce..69858dc687d4783f751a12daa8b78dee1e3c7b39 100644 (file)
@@ -18,6 +18,7 @@ Revision history
    2011/03/20 - update: implementation and security considerations
    2012/06/21 - add support for binary format
    2012/11/19 - final review and fixes
+   2014/05/18 - modify and extend PROXY protocol version 2
 
 
 1. Background
@@ -326,25 +327,27 @@ protocol signature :
 Note that this block contains a null byte at the 5th position, so it must not
 be handled as a null-terminated string.
 
-The next byte (the 13th one) is the protocol version. As of this specification,
-it must always be sent as \x02 and the receiver must only accept this value.
+The next byte (the 13th one) is the protocol version and command.
 
-The 14th byte represents the command :
-  - \x00 : LOCAL : the connection was established on purpose by the proxy
+The highest four bits contains the version. As of this specification, it must
+always be sent as \x2 and the receiver must only accept this value.
+
+The lowest four bits represents the command :
+  - \x0 : LOCAL : the connection was established on purpose by the proxy
     without being relayed. The connection endpoints are the sender and the
     receiver. Such connections exist when the proxy sends health-checks to the
     server. The receiver must accept this connection as valid and must use the
     real connection endpoints and discard the protocol block including the
     family which is ignored.
 
-  - \x01 : PROXY : the connection was established on behalf of another node,
+  - \x1 : PROXY : the connection was established on behalf of another node,
     and reflects the original connection endpoints. The receiver must then use
     the information provided in the protocol block to get original the address.
 
   - other values are unassigned and must not be emitted by senders. Receivers
     must drop connections presenting unexpected values here.
 
-The 15th byte contains the transport protocol and address family. The highest 4
+The 14th byte contains the transport protocol and address family. The highest 4
 bits contain the address family, the lowest 4 bits contain the protocol.
 
 The address family maps to the original socket family without necessarily
@@ -370,7 +373,7 @@ matching the values internally used by the system. It may be one of :
   - other values are unspecified and must not be emitted in version 2 of this
     protocol and must be rejected as invalid by receivers.
 
-The transport protocol is specified in the lowest 4 bits of the the 15th byte :
+The transport protocol is specified in the lowest 4 bits of the the 14th byte :
 
   - 0x0 : UNSPEC : the connection is forwarded for an unknown, unspecified
     or unsupported protocol. The sender should use this family when sending
@@ -424,11 +427,10 @@ Only the UNSPEC protocol byte (\x00) is mandatory. A receiver is not required
 to implement other ones, provided that it automatically falls back to the
 UNSPEC mode for the valid combinations above that it does not support.
 
-The 16th byte is the address length in bytes. It is used so that the receiver
-knows how many address bytes to skip even when it does not implement the
-presented protocol. Thus the length of the protocol header in bytes is always
-exactly 16 + this byte. This means that the largest protocol header may only
-be 16 + 255 = 271 bytes, which fits in a usual MSS. When a sender presents a
+The 15th and 16th bytes is the address length in bytes in network endien order.
+It is used so that the receiver knows how many address bytes to skip even when
+it does not implement the presented protocol. Thus the length of the protocol
+header in bytes is always exactly 16 + this value. When a sender presents a
 LOCAL connection, it should not present any address so it sets this field to
 zero. Receivers MUST always consider this field to skip the appropriate number
 of bytes and must not assume zero is presented for LOCAL connections. When a
@@ -439,10 +441,9 @@ So the 16-byte version 2 header can be described this way :
 
     struct proxy_hdr_v2 {
         uint8_t sig[12];  /* hex 0D 0A 0D 0A 00 0D 0A 51 55 49 54 0A */
-        uint8_t ver;      /* hex 02 */
-        uint8_t cmd;      /* hex 00 or 01 */
+        uint8_t ver;      /* protocol version and command */
         uint8_t fam;      /* protocol family and address */
-        uint8_t len;      /* number of following bytes part of the header */
+        uint16_t len;     /* number of following bytes part of the header */
     };
 
 Starting from the 17th byte, addresses are presented in network byte order.
@@ -499,6 +500,24 @@ protocol. Identifying the protocol version is easy :
     - otherwise the protocol is not covered by this specification and the
       connection must be dropped.
 
+If the length specified in the PROXY protocol header indicates that additional
+bytes are part of the header beyond the address information, a receiver may
+choose to skip over and ignore those bytes, or attempt to interpret those
+bytes.
+
+The information in those bytes will be arranged in Type-Length-Value (TLV
+vectors) in the following format.  The first byte is the Type of the vector.
+The second two bytes represent the length in bytes of the value (not included
+the Type and Length bytes), and following the length field is the number of
+bytes specified by the length.
+
+        struct {
+            uint8_t type;
+            uint8_t length_hi;
+            uint8_t length_lo;
+            uint8_t value[0];
+        } tlv;
+
 
 3. Implementations
 
@@ -516,6 +535,9 @@ Haproxy 1.5 implements version 1 of the PROXY protocol on both sides :
     "accept-proxy", then the relayed information is the one advertised in this
     connection's PROXY line.
 
+  - Haproxy 1.5 also implements version 2 of the PROXY protocol as a sender. In
+    addition, a TLV with limited, optional, SSL information has been added.
+
 Stunnel added support for version 1 of the protocol for outgoing connections in
 version 4.45.
 
index 10bc4d8ac32ede59cf67913e9b319c238027d68d..c9972b1550f7c963e774ee2e706b68367f2e929d 100644 (file)
@@ -41,7 +41,9 @@ int conn_fd_handler(int fd);
 
 /* receive a PROXY protocol header over a connection */
 int conn_recv_proxy(struct connection *conn, int flag);
-int make_proxy_line(char *buf, int buf_len, struct sockaddr_storage *src, struct sockaddr_storage *dst);
+int make_proxy_line(char *buf, int buf_len, struct server *srv, struct connection *remote);
+int make_proxy_line_v1(char *buf, int buf_len, struct sockaddr_storage *src, struct sockaddr_storage *dst);
+int make_proxy_line_v2(char *buf, int buf_len, struct server *srv, struct connection *remote);
 
 /* returns true is the transport layer is ready */
 static inline int conn_xprt_ready(const struct connection *conn)
index 9d891d90857a5e6ed65513d80c404544668ba175..2d1dadc6d731425d6e2760363b0494cadc4c27b8 100644 (file)
@@ -32,6 +32,16 @@ extern struct xprt_ops ssl_sock;
 extern int sslconns;
 extern int totalsslconns;
 
+/* boolean, returns true if connection is over SSL */
+static inline
+int ssl_sock_is_ssl(struct connection *conn)
+{
+       if (!conn || conn->xprt != &ssl_sock || !conn->xprt_ctx)
+               return 0;
+       else
+               return 1;
+}
+
 int ssl_sock_handshake(struct connection *conn, unsigned int flag);
 int ssl_sock_prepare_ctx(struct bind_conf *bind_conf, SSL_CTX *ctx, struct proxy *proxy);
 void ssl_sock_free_certs(struct bind_conf *bind_conf);
@@ -40,6 +50,10 @@ int ssl_sock_prepare_srv_ctx(struct server *srv, struct proxy *px);
 void ssl_sock_free_all_ctx(struct bind_conf *bind_conf);
 const char *ssl_sock_get_cipher_name(struct connection *conn);
 const char *ssl_sock_get_proto_version(struct connection *conn);
+char *ssl_sock_get_version(struct connection *conn);
+int ssl_sock_get_cert_used(struct connection *conn);
+char *ssl_sock_get_common_name(struct connection *conn);
+unsigned int ssl_sock_get_verify_result(struct connection *conn);
 
 #endif /* _PROTO_SSL_SOCK_H */
 
index 83ac432b340a5948b8c2cbc88d8575b2c47578e7..4779bc037554c6ec0e1173f376cb70e898edcbda 100644 (file)
@@ -267,6 +267,77 @@ struct connection {
        } addr; /* addresses of the remote side, client for producer and server for consumer */
 };
 
+/* proxy protocol v2 definitions */
+#define PP2_SIGNATURE_LEN        12
+#define PP2_HEADER_LEN           16
+#define PP2_VERSION            0x20
+#define PP2_CMD_LOCAL          0x00
+#define PP2_CMD_PROXY          0x01
+#define PP2_FAM_UNSPEC         0x00
+#define PP2_FAM_INET           0x10
+#define PP2_FAM_INET6          0x20
+#define PP2_FAM_UNIX           0x30
+#define PP2_TRANS_UNSPEC       0x00
+#define PP2_TRANS_STREAM       0x01
+#define PP2_TRANS_DGRAM        0x02
+
+#define PP2_ADDR_LEN_UNSPEC       0
+#define PP2_ADDR_LEN_INET        12
+#define PP2_ADDR_LEN_INET6       36
+#define PP2_ADDR_LEN_UNIX       216
+
+#define PP2_HDR_LEN_UNSPEC  (PP2_HEADER_LEN + PP2_ADDR_LEN_UNSPEC)
+#define PP2_HDR_LEN_INET    (PP2_HEADER_LEN + PP2_ADDR_LEN_INET)
+#define PP2_HDR_LEN_INET6   (PP2_HEADER_LEN + PP2_ADDR_LEN_INET6)
+#define PP2_HDR_LEN_UNIX    (PP2_HEADER_LEN + PP2_ADDR_LEN_UNIX)
+
+struct proxy_hdr_v2 {
+       uint8_t sig[12];   /* hex 0D 0A 0D 0A 00 0D 0A 51 55 49 54 0A */
+       uint8_t cmd;       /* protocol version and command */
+       uint8_t fam;       /* protocol family and transport */
+       uint16_t len;      /* number of following bytes part of the header */
+};
+
+union proxy_addr {
+       struct {        /* for TCP/UDP over IPv4, len = 12 */
+               uint32_t src_addr;
+               uint32_t dst_addr;
+               uint16_t src_port;
+               uint16_t dst_port;
+       } ipv4_addr;
+       struct {        /* for TCP/UDP over IPv6, len = 36 */
+               uint8_t  src_addr[16];
+               uint8_t  dst_addr[16];
+               uint16_t src_port;
+               uint16_t dst_port;
+       } ipv6_addr;
+       struct {        /* for AF_UNIX sockets, len = 216 */
+               uint8_t src_addr[108];
+               uint8_t dst_addr[108];
+       } unix_addr;
+};
+
+#define PP2_TYPE_SSL           0x20
+#define PP2_TYPE_SSL_VERSION   0x21
+#define PP2_TYPE_SSL_CN        0x22
+
+struct tlv {
+       uint8_t type;
+       uint8_t length_hi;
+       uint8_t length_lo;
+       uint8_t value[0];
+}__attribute__((packed));
+
+struct tlv_ssl {
+       struct tlv tlv;
+       uint8_t client;
+       uint32_t verify;
+       uint8_t sub_tlv[0];
+}__attribute__((packed));
+
+#define PP2_CLIENT_SSL      0x01
+#define PP2_CLIENT_CERT     0x02
+
 #endif /* _TYPES_CONNECTION_H */
 
 /*
index 54ab8133c71b4898b6fa88437c19750411de0a34..7a977e3e79c71b51fc87a5c55838776f3bbb54e6 100644 (file)
 #define SRV_WARMINGUP  0x0040  /* this server is warming up after a failure */
 #define SRV_MAINTAIN   0x0080  /* this server is in maintenance mode */
 #define SRV_DRAIN      0x0100  /* this server has been requested to drain its connections */
-/* unused: 0x0200, 0x0400 */
-#define SRV_SEND_PROXY 0x0800  /* this server talks the PROXY protocol */
+/* unused: 0x0200, 0x0400, 0x0800 */
 #define SRV_NON_STICK  0x1000  /* never add connections allocated to this server to a stick table */
 
+/* configured server options for send-proxy (server->pp_opts) */
+#define SRV_PP_V1          0x0001        /* proxy protocol version 1 */
+#define SRV_PP_V2          0x0002        /* proxy protocol version 2 */
+#define SRV_PP_V2_SSL      0x0004        /* proxy protocol version 2 with SSL*/
+#define SRV_PP_V2_SSL_CN   0x0008        /* proxy protocol version 2 with SSL and CN*/
+
 /* function which act on servers need to return various errors */
 #define SRV_STATUS_OK       0   /* everything is OK. */
 #define SRV_STATUS_INTERNAL 1   /* other unrecoverable errors. */
@@ -106,6 +111,7 @@ struct server {
        int rdr_len;                            /* the length of the redirection prefix */
        char *cookie;                           /* the id set in the cookie */
        char *rdr_pfx;                          /* the redirection prefix */
+       int pp_opts;                            /* proxy protocol options (SRV_PP_*) */
 
        struct proxy *proxy;                    /* the proxy this server belongs to */
        int served;                             /* # of active sessions currently being served (ie not pending) */
index bc63903fc6772657e74b0b233f046fcd3981ea40..24d8142d73021e8f6c680d39b6da8655a87e0f0d 100644 (file)
@@ -1059,7 +1059,7 @@ int connect_server(struct session *s)
 
                /* process the case where the server requires the PROXY protocol to be sent */
                srv_conn->send_proxy_ofs = 0;
-               if (objt_server(s->target) && (objt_server(s->target)->state & SRV_SEND_PROXY)) {
+               if (objt_server(s->target) && objt_server(s->target)->pp_opts) {
                        srv_conn->send_proxy_ofs = 1; /* must compute size */
                        cli_conn = objt_conn(s->req->prod->end);
                        if (cli_conn)
index 1483f1871ec9dc1ad579542411b116bc820f1897..91ad0b63ddc4dfc299df9b869418045d2ada073c 100644 (file)
@@ -437,6 +437,23 @@ int conn_recv_proxy(struct connection *conn, int flag)
        return 0;
 }
 
+int make_proxy_line(char *buf, int buf_len, struct server *srv, struct connection *remote)
+{
+       int ret = 0;
+
+       if (srv && (srv->pp_opts & SRV_PP_V2)) {
+               ret = make_proxy_line_v2(buf, buf_len, srv, remote);
+       }
+       else {
+               if (remote)
+                       ret = make_proxy_line_v1(buf, buf_len, &remote->addr.from, &remote->addr.to);
+               else
+                       ret = make_proxy_line_v1(buf, buf_len, NULL, NULL);
+       }
+
+       return ret;
+}
+
 /* Makes a PROXY protocol line from the two addresses. The output is sent to
  * buffer <buf> for a maximum size of <buf_len> (including the trailing zero).
  * It returns the number of bytes composing this line (including the trailing
@@ -444,7 +461,7 @@ int conn_recv_proxy(struct connection *conn, int flag)
  * TCP6 and "UNKNOWN" formats. If any of <src> or <dst> is null, UNKNOWN is
  * emitted as well.
  */
-int make_proxy_line(char *buf, int buf_len, struct sockaddr_storage *src, struct sockaddr_storage *dst)
+int make_proxy_line_v1(char *buf, int buf_len, struct sockaddr_storage *src, struct sockaddr_storage *dst)
 {
        int ret = 0;
 
@@ -516,3 +533,113 @@ int make_proxy_line(char *buf, int buf_len, struct sockaddr_storage *src, struct
        }
        return ret;
 }
+
+#ifdef USE_OPENSSL
+static int make_tlv(char *dest, int dest_len, char type, uint16_t length, char *value)
+{
+       struct tlv *tlv;
+
+       if (!dest || (length + sizeof(*tlv) > dest_len))
+               return 0;
+
+       tlv = (struct tlv *)dest;
+
+       tlv->type = type;
+       tlv->length_hi = length >> 8;
+       tlv->length_lo = length & 0x00ff;
+       memcpy(tlv->value, value, length);
+       return length + sizeof(*tlv);
+}
+#endif
+
+int make_proxy_line_v2(char *buf, int buf_len, struct server *srv, struct connection *remote)
+{
+       const char pp2_signature[12] = {0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A};
+       int ret = 0;
+       struct proxy_hdr_v2 *hdr_p = (struct proxy_hdr_v2 *)buf;
+       union proxy_addr *addr_p = (union proxy_addr *)(buf + PP2_HEADER_LEN);
+       struct sockaddr_storage null_addr = {0};
+       struct sockaddr_storage *src = &null_addr;
+       struct sockaddr_storage *dst = &null_addr;
+#ifdef USE_OPENSSL
+       int tlv_len = 0;
+       char *value = NULL;
+       struct tlv_ssl *tlv;
+       int ssl_tlv_len = 0;
+#endif
+
+       if (buf_len < PP2_HEADER_LEN)
+               return 0;
+       memcpy(hdr_p->sig, pp2_signature, PP2_SIGNATURE_LEN);
+
+       if (remote) {
+               src = &remote->addr.from;
+               dst = &remote->addr.to;
+       }
+       if (src && dst && src->ss_family == dst->ss_family && src->ss_family == AF_INET) {
+               if (buf_len < PP2_HDR_LEN_INET)
+                       return 0;
+               hdr_p->cmd = PP2_VERSION | PP2_CMD_PROXY;
+               hdr_p->fam = PP2_FAM_INET | PP2_TRANS_STREAM;
+               addr_p->ipv4_addr.src_addr = ((struct sockaddr_in *)src)->sin_addr.s_addr;
+               addr_p->ipv4_addr.dst_addr = ((struct sockaddr_in *)dst)->sin_addr.s_addr;
+               addr_p->ipv4_addr.src_port = ((struct sockaddr_in *)src)->sin_port;
+               addr_p->ipv4_addr.dst_port = ((struct sockaddr_in *)dst)->sin_port;
+               ret = PP2_HDR_LEN_INET;
+       }
+       else if (src && dst && src->ss_family == dst->ss_family && src->ss_family == AF_INET6) {
+               if (buf_len < PP2_HDR_LEN_INET6)
+                       return 0;
+               hdr_p->cmd = PP2_VERSION | PP2_CMD_PROXY;
+               hdr_p->fam = PP2_FAM_INET6 | PP2_TRANS_STREAM;
+               memcpy(addr_p->ipv6_addr.src_addr, &((struct sockaddr_in6 *)src)->sin6_addr, 16);
+               memcpy(addr_p->ipv6_addr.dst_addr, &((struct sockaddr_in6 *)dst)->sin6_addr, 16);
+               addr_p->ipv6_addr.src_port = ((struct sockaddr_in6 *)src)->sin6_port;
+               addr_p->ipv6_addr.dst_port = ((struct sockaddr_in6 *)dst)->sin6_port;
+               ret = PP2_HDR_LEN_INET6;
+       }
+       else {
+               if (buf_len < PP2_HDR_LEN_UNSPEC)
+                       return 0;
+               hdr_p->cmd = PP2_VERSION | PP2_CMD_LOCAL;
+               hdr_p->fam = PP2_FAM_UNSPEC | PP2_TRANS_UNSPEC;
+               ret = PP2_HDR_LEN_UNSPEC;
+       }
+
+#ifdef USE_OPENSSL
+       if (srv->pp_opts & SRV_PP_V2_SSL) {
+               if ((buf_len - ret) < sizeof(struct tlv_ssl))
+                       return 0;
+               tlv = (struct tlv_ssl *)&buf[ret];
+               memset(tlv, 0, sizeof(struct tlv_ssl));
+               ssl_tlv_len += sizeof(struct tlv_ssl);
+               tlv->tlv.type = PP2_TYPE_SSL;
+               if (ssl_sock_is_ssl(remote)) {
+                       tlv->client |= PP2_CLIENT_SSL;
+                       value = ssl_sock_get_version(remote);
+                       if (value) {
+                               tlv_len = make_tlv(&buf[ret+ssl_tlv_len], (buf_len-ret-ssl_tlv_len), PP2_TYPE_SSL_VERSION, strlen(value), value);
+                               ssl_tlv_len += tlv_len;
+                       }
+                       if (ssl_sock_get_cert_used(remote)) {
+                               tlv->client |= PP2_CLIENT_CERT;
+                               tlv->verify = htonl(ssl_sock_get_verify_result(remote));
+                       }
+                       if (srv->pp_opts & SRV_PP_V2_SSL_CN) {
+                               value = ssl_sock_get_common_name(remote);
+                               if (value) {
+                                       tlv_len = make_tlv(&buf[ret+ssl_tlv_len], (buf_len - ret - ssl_tlv_len), PP2_TYPE_SSL_CN, strlen(value), value);
+                                       ssl_tlv_len += tlv_len;
+                               }
+                       }
+               }
+               tlv->tlv.length_hi = (uint16_t)(ssl_tlv_len - sizeof(struct tlv)) >> 8;
+               tlv->tlv.length_lo = (uint16_t)(ssl_tlv_len - sizeof(struct tlv)) & 0x00ff;
+               ret += ssl_tlv_len;
+       }
+#endif
+
+       hdr_p->len = htons((uint16_t)(ret - PP2_HEADER_LEN));
+
+       return ret;
+}
index 5ac5e373dc1817e67ffbda410e98712ed1b3e764..565a108be8043b84b5ef747eba4a159b13470a55 100644 (file)
@@ -612,7 +612,11 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr
                                cur_arg ++;
                        }
                        else if (!defsrv && !strcmp(args[cur_arg], "send-proxy")) {
-                               newsrv->state |= SRV_SEND_PROXY;
+                               newsrv->pp_opts |= SRV_PP_V1;
+                               cur_arg ++;
+                       }
+                       else if (!defsrv && !strcmp(args[cur_arg], "send-proxy-v2")) {
+                               newsrv->pp_opts |= SRV_PP_V2;
                                cur_arg ++;
                        }
                        else if (!defsrv && !strcmp(args[cur_arg], "check-send-proxy")) {
@@ -1043,7 +1047,7 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr
 #ifdef USE_OPENSSL
                                newsrv->check.use_ssl |= (newsrv->use_ssl || (newsrv->proxy->options & PR_O_TCPCHK_SSL));
 #endif
-                               newsrv->check.send_proxy |= (newsrv->state & SRV_SEND_PROXY);
+                               newsrv->check.send_proxy |= (newsrv->pp_opts);
                        }
                        /* try to get the port from check_core.addr if check.port not set */
                        if (!newsrv->check.port)
index dae8a3879d8a2cb8bee9502cbb4ccb34ee1b1565..fd0b41fd60867963f8974dc6d4517019099b609b 100644 (file)
@@ -1871,6 +1871,70 @@ ssl_sock_get_dn_oneline(X509_NAME *a, struct chunk *out)
        return 1;
 }
 
+char *ssl_sock_get_version(struct connection *conn)
+{
+       if (!ssl_sock_is_ssl(conn))
+               return NULL;
+
+       return (char *)SSL_get_version(conn->xprt_ctx);
+}
+
+/* returns common name, NULL terminated, from client certificate, or NULL if none */
+char *ssl_sock_get_common_name(struct connection *conn)
+{
+       X509 *crt = NULL;
+       X509_NAME *name;
+       struct chunk *cn_trash;
+       const char find_cn[] = "CN";
+       const struct chunk find_cn_chunk = {
+               .str = (char *)&find_cn,
+               .len = sizeof(find_cn)-1
+       };
+       char *result = NULL;
+
+       if (!ssl_sock_is_ssl(conn))
+               return NULL;
+
+       /* SSL_get_peer_certificate, it increase X509 * ref count */
+       crt = SSL_get_peer_certificate(conn->xprt_ctx);
+       if (!crt)
+               goto out;
+
+       name = X509_get_subject_name(crt);
+       if (!name)
+               goto out;
+
+       cn_trash = get_trash_chunk();
+       if (ssl_sock_get_dn_entry(name, &find_cn_chunk, 1, cn_trash) <= 0)
+               goto out;
+       cn_trash->str[cn_trash->len] = '\0';
+       result = cn_trash->str;
+
+       out:
+       if (crt)
+               X509_free(crt);
+
+       return result;
+}
+
+/* returns 1 if client passed a certificate, 0 if not */
+int ssl_sock_get_cert_used(struct connection *conn)
+{
+       if (!ssl_sock_is_ssl(conn))
+               return 0;
+
+       return SSL_SOCK_ST_FL_VERIFY_DONE & conn->xprt_st ? 1 : 0;
+}
+
+/* returns result from SSL verify */
+unsigned int ssl_sock_get_verify_result(struct connection *conn)
+{
+       if (!ssl_sock_is_ssl(conn))
+               return (unsigned int)X509_V_ERR_APPLICATION_VERIFICATION;
+
+       return (unsigned int)SSL_get_verify_result(conn->xprt_ctx);
+}
+
 /***** Below are some sample fetching functions for ACL/patterns *****/
 
 /* boolean, returns true if client cert was present */
@@ -3349,6 +3413,22 @@ static int srv_parse_no_tls_tickets(char **args, int *cur_arg, struct proxy *px,
        newsrv->ssl_ctx.options |= SRV_SSL_O_NO_TLS_TICKETS;
        return 0;
 }
+/* parse the "send-proxy-v2-ssl" server keyword */
+static int srv_parse_send_proxy_ssl(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err)
+{
+       newsrv->pp_opts |= SRV_PP_V2;
+       newsrv->pp_opts |= SRV_PP_V2_SSL;
+       return 0;
+}
+
+/* parse the "send-proxy-v2-ssl-cn" server keyword */
+static int srv_parse_send_proxy_cn(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err)
+{
+       newsrv->pp_opts |= SRV_PP_V2;
+       newsrv->pp_opts |= SRV_PP_V2_SSL;
+       newsrv->pp_opts |= SRV_PP_V2_SSL_CN;
+       return 0;
+}
 
 /* parse the "ssl" server keyword */
 static int srv_parse_ssl(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err)
@@ -3513,6 +3593,8 @@ static struct srv_kw_list srv_kws = { "SSL", { }, {
        { "no-tlsv11",             srv_parse_no_tlsv11,      0, 0 }, /* disable TLSv11 */
        { "no-tlsv12",             srv_parse_no_tlsv12,      0, 0 }, /* disable TLSv12 */
        { "no-tls-tickets",        srv_parse_no_tls_tickets, 0, 0 }, /* disable session resumption tickets */
+       { "send-proxy-v2-ssl",     srv_parse_send_proxy_ssl, 0, 0 }, /* send PROXY protocol header v2 with SSL info */
+       { "send-proxy-v2-ssl-cn",  srv_parse_send_proxy_cn,  0, 0 }, /* send PROXY protocol header v2 with CN */
        { "ssl",                   srv_parse_ssl,            0, 0 }, /* enable SSL processing */
        { "verify",                srv_parse_verify,         1, 0 }, /* set SSL verify method */
        { "verifyhost",            srv_parse_verifyhost,     1, 0 }, /* require that SSL cert verifies for hostname */
index f23a9b0d12e1638fbbe5cb8333d1455484ed9e84..67a5234fa86ebddfbf5e0efd275d070bec95620e 100644 (file)
@@ -422,11 +422,7 @@ int conn_si_send_proxy(struct connection *conn, unsigned int flag)
                if (conn->data == &si_conn_cb) {
                        struct stream_interface *si = conn->owner;
                        struct connection *remote = objt_conn(si->ob->prod->end);
-
-                       if (remote)
-                               ret = make_proxy_line(trash.str, trash.size, &remote->addr.from, &remote->addr.to);
-                       else
-                               ret = make_proxy_line(trash.str, trash.size, NULL, NULL);
+                       ret = make_proxy_line(trash.str, trash.size, objt_server(conn->target), remote);
                }
                else {
                        /* The target server expects a LOCAL line to be sent first. Retrieving
@@ -440,7 +436,7 @@ int conn_si_send_proxy(struct connection *conn, unsigned int flag)
                        if (!(conn->flags & CO_FL_ADDR_TO_SET))
                                goto out_wait;
 
-                       ret = make_proxy_line(trash.str, trash.size, &conn->addr.from, &conn->addr.to);
+                       ret = make_proxy_line(trash.str, trash.size, objt_server(conn->target), conn);
                }
 
                if (!ret)