aboutsummaryrefslogtreecommitdiff
path: root/net/haproxy/patches/001-haproxy-1.4.x-sendproxy.patch
blob: 401d57ffd62da983ccc868166ac699e5f9ae2ef2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
From af2038557a14bf6e2915bed545e216a0f1a95fc5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cyril=20Bont=C3=A9?= <cyril.bonte@free.fr>
Date: Mon, 15 Apr 2013 22:05:00 +0200
Subject: [PATCH] Proxy Protocol based on haproxy 1.4.23

---
 doc/configuration.txt     |  26 ++++++-
 include/common/standard.h |  25 ++++++-
 include/proto/client.h    |   1 +
 include/types/buffers.h   |  20 ++---
 include/types/protocols.h |   1 +
 src/cfgparse.c            |  15 +++-
 src/client.c              | 186 ++++++++++++++++++++++++++++++++++++++++++++++
 src/proto_http.c          |   4 +-
 src/session.c             |   7 ++
 src/standard.c            |   9 ++-
 10 files changed, 275 insertions(+), 19 deletions(-)

Index: haproxy-1.4.25/doc/configuration.txt
===================================================================
--- haproxy-1.4.25.orig/doc/configuration.txt
+++ haproxy-1.4.25/doc/configuration.txt
@@ -1343,6 +1343,7 @@ bind [<address>]:<port_range> [, ...] tr
 bind [<address>]:<port_range> [, ...] id <id>
 bind [<address>]:<port_range> [, ...] name <name>
 bind [<address>]:<port_range> [, ...] defer-accept
+bind [<address>]:<port_range> [, ...] accept-proxy
   Define one or several listening addresses and/or ports in a frontend.
   May be used in sections :   defaults | frontend | listen | backend
                                   no   |    yes   |   yes  |   no
@@ -1423,6 +1424,19 @@ bind [<address>]:<port_range> [, ...] de
                   with front firewalls which would see an established
                   connection while the proxy will only see it in SYN_RECV.
 
+    accept-proxy  is an optional keyword which enforces use of the PROXY
+                  protocol over any connection accepted by this listener. The
+                  PROXY protocol dictates the layer 3/4 addresses of the
+                  incoming connection to be used everywhere an address is used,
+                  with the only exception of "tcp-request connection" rules
+                  which will only see the real connection address. Logs will
+                  reflect the addresses indicated in the protocol, unless it is
+                  violated, in which case the real address will still be used.
+                  This keyword combined with support from external components
+                  can be used as an efficient and reliable alternative to the
+                  X-Forwarded-For mechanism which is not always reliable and
+                  not even always usable.
+
   It is possible to specify a list of address:port combinations delimited by
   commas. The frontend will then listen on all of these addresses. There is no
   fixed limit to the number of addresses and ports which can be listened on in
@@ -1433,8 +1447,10 @@ bind [<address>]:<port_range> [, ...] de
         listen http_proxy
             bind :80,:443
             bind 10.0.0.1:10080,10.0.0.1:10443
+            bind 127.0.0.1:8443 accept-proxy
 
-  See also : "source".
+  See also : "source", "option forwardfor" and the PROXY protocol
+             documentation.
 
 
 bind-process [ all | odd | even | <number 1-32> ] ...
@@ -7257,7 +7273,9 @@ marked with a star ('*') after the field
 
 Detailed fields description :
   - "client_ip" is the IP address of the client which initiated the TCP
-    connection to haproxy.
+    connection to haproxy. Note that when the connection is accepted on a
+    socket configured with "accept-proxy" and the PROXY protocol is correctly
+    used, then the logs will reflect the forwarded connection's information.
 
   - "client_port" is the TCP port of the client which initiated the connection.
 
@@ -7430,7 +7448,9 @@ with a star ('*') after the field name b
 
 Detailed fields description :
   - "client_ip" is the IP address of the client which initiated the TCP
-    connection to haproxy.
+    connection to haproxy. Note that when the connection is accepted on a
+    socket configured with "accept-proxy" and the PROXY protocol is correctly
+    used, then the logs will reflect the forwarded connection's information.
 
   - "client_port" is the TCP port of the client which initiated the connection.
 
Index: haproxy-1.4.25/include/common/standard.h
===================================================================
--- haproxy-1.4.25.orig/include/common/standard.h
+++ haproxy-1.4.25/include/common/standard.h
@@ -269,6 +269,28 @@ static inline unsigned int __strl2uic(co
 	return i;
 }
 
+/* This function reads an unsigned integer from the string pointed to by <s>
+ * and returns it. The <s> pointer is adjusted to point to the first unread
+ * char. The function automatically stops at <end>.
+ */
+static inline unsigned int __read_uint(const char **s, const char *end)
+{
+	const char *ptr = *s;
+	unsigned int i = 0;
+	unsigned int j, k;
+
+	while (ptr < end) {
+		j = *ptr - '0';
+		k = i * 10;
+		if (j > 9)
+			break;
+		i = k + j;
+		ptr++;
+	}
+	*s = ptr;
+	return i;
+}
+
 extern unsigned int str2ui(const char *s);
 extern unsigned int str2uic(const char *s);
 extern unsigned int strl2ui(const char *s, int len);
@@ -276,9 +298,10 @@ extern unsigned int strl2uic(const char 
 extern int strl2ic(const char *s, int len);
 extern int strl2irc(const char *s, int len, int *ret);
 extern int strl2llrc(const char *s, int len, long long *ret);
+extern unsigned int read_uint(const char **s, const char *end);
 unsigned int inetaddr_host(const char *text);
 unsigned int inetaddr_host_lim(const char *text, const char *stop);
-unsigned int inetaddr_host_lim_ret(const char *text, char *stop, const char **ret);
+unsigned int inetaddr_host_lim_ret(char *text, char *stop, char **ret);
 
 static inline char *cut_crlf(char *s) {
 
Index: haproxy-1.4.25/include/proto/client.h
===================================================================
--- haproxy-1.4.25.orig/include/proto/client.h
+++ haproxy-1.4.25/include/proto/client.h
@@ -25,6 +25,7 @@
 #include <common/config.h>
 #include <types/session.h>
 
+int frontend_decode_proxy_request(struct session *s, struct buffer *req, int an_bit);
 void get_frt_addr(struct session *s);
 int event_accept(int fd);
 
Index: haproxy-1.4.25/include/types/buffers.h
===================================================================
--- haproxy-1.4.25.orig/include/types/buffers.h
+++ haproxy-1.4.25/include/types/buffers.h
@@ -135,16 +135,16 @@
  * The field is blanked by buffer_init() and only by analysers themselves
  * afterwards.
  */
-#define AN_REQ_INSPECT          0x00000001  /* inspect request contents */
-#define AN_REQ_WAIT_HTTP        0x00000002  /* wait for an HTTP request */
-#define AN_REQ_HTTP_PROCESS_FE  0x00000004  /* process the frontend's HTTP part */
-#define AN_REQ_SWITCHING_RULES  0x00000008  /* apply the switching rules */
-#define AN_REQ_HTTP_PROCESS_BE  0x00000010  /* process the backend's HTTP part */
-#define AN_REQ_HTTP_INNER       0x00000020  /* inner processing of HTTP request */
-#define AN_REQ_HTTP_TARPIT      0x00000040  /* wait for end of HTTP tarpit */
-#define AN_REQ_HTTP_BODY        0x00000080  /* inspect HTTP request body */
-#define AN_REQ_STICKING_RULES   0x00000100  /* table persistence matching */
-/* unused: 0x200 */
+#define AN_REQ_DECODE_PROXY     0x00000001  /* take the proxied address from a 'PROXY' line */
+#define AN_REQ_INSPECT          0x00000002  /* inspect request contents */
+#define AN_REQ_WAIT_HTTP        0x00000004  /* wait for an HTTP request */
+#define AN_REQ_HTTP_PROCESS_FE  0x00000008  /* process the frontend's HTTP part */
+#define AN_REQ_SWITCHING_RULES  0x00000010  /* apply the switching rules */
+#define AN_REQ_HTTP_PROCESS_BE  0x00000020  /* process the backend's HTTP part */
+#define AN_REQ_HTTP_INNER       0x00000040  /* inner processing of HTTP request */
+#define AN_REQ_HTTP_TARPIT      0x00000080  /* wait for end of HTTP tarpit */
+#define AN_REQ_HTTP_BODY        0x00000100  /* inspect HTTP request body */
+#define AN_REQ_STICKING_RULES   0x00000200  /* table persistence matching */
 #define AN_REQ_PRST_RDP_COOKIE  0x00000400  /* persistence on rdp cookie */
 #define AN_REQ_HTTP_XFER_BODY   0x00000800  /* forward request body */
 
Index: haproxy-1.4.25/include/types/protocols.h
===================================================================
--- haproxy-1.4.25.orig/include/types/protocols.h
+++ haproxy-1.4.25/include/types/protocols.h
@@ -72,6 +72,7 @@
 #define LI_O_FOREIGN	0x0002	/* permit listening on foreing addresses */
 #define LI_O_NOQUICKACK	0x0004	/* disable quick ack of immediate data (linux) */
 #define LI_O_DEF_ACCEPT	0x0008	/* wait up to 1 second for data before accepting */
+#define LI_O_ACC_PROXY  0x0010  /* find the proxied address in the first request line */
 
 /* The listener will be directly referenced by the fdtab[] which holds its
  * socket. The listener provides the protocol-specific accept() function to
Index: haproxy-1.4.25/src/cfgparse.c
===================================================================
--- haproxy-1.4.25.orig/src/cfgparse.c
+++ haproxy-1.4.25/src/cfgparse.c
@@ -1467,6 +1467,16 @@ int cfg_parse_listen(const char *file, i
 #endif
 			}
 
+			if (!strcmp(args[cur_arg], "accept-proxy")) { /* expect a 'PROXY' line first */
+				struct listener *l;
+
+				for (l = curproxy->listen; l != last_listen; l = l->next)
+					l->options |= LI_O_ACC_PROXY;
+
+				cur_arg ++;
+				continue;
+			}
+
 			if (!strcmp(args[cur_arg], "name")) {
 				struct listener *l;
 
@@ -1519,7 +1529,7 @@ int cfg_parse_listen(const char *file, i
 				continue;
 			}
 
-			Alert("parsing [%s:%d] : '%s' only supports the 'transparent', 'defer-accept', 'name', 'id', 'mss' and 'interface' options.\n",
+			Alert("parsing [%s:%d] : '%s' only supports the 'transparent', 'accept-proxy', 'defer-accept', 'name', 'id', 'mss' and 'interface' options.\n",
 			      file, linenum, args[0]);
 			err_code |= ERR_ALERT | ERR_FATAL;
 			goto out;
@@ -5743,6 +5753,9 @@ out_uri_auth_compat:
 			listener->handler = process_session;
 			listener->analysers |= curproxy->fe_req_ana;
 
+			if (listener->options & LI_O_ACC_PROXY)
+				listener->analysers |= AN_REQ_DECODE_PROXY;
+
 			/* smart accept mode is automatic in HTTP mode */
 			if ((curproxy->options2 & PR_O2_SMARTACC) ||
 			    (curproxy->mode == PR_MODE_HTTP &&
Index: haproxy-1.4.25/src/client.c
===================================================================
--- haproxy-1.4.25.orig/src/client.c
+++ haproxy-1.4.25/src/client.c
@@ -22,6 +22,7 @@
 
 #include <common/compat.h>
 #include <common/config.h>
+#include <common/debug.h>
 #include <common/time.h>
 
 #include <types/global.h>
@@ -43,6 +44,191 @@
 #include <proto/task.h>
 
 
+/* This analyser tries to fetch a line from the request buffer which looks like :
+ *
+ *   "PROXY" <SP> PROTO <SP> SRC3 <SP> DST3 <SP> SRC4 <SP> <DST4> "\r\n"
+ *
+ * There must be exactly one space between each field. Fields are :
+ *  - PROTO : layer 4 protocol, which must be "TCP4" or "TCP6".
+ *  - SRC3  : layer 3 (eg: IP) source address in standard text form
+ *  - DST3  : layer 3 (eg: IP) destination address in standard text form
+ *  - SRC4  : layer 4 (eg: TCP port) source address in standard text form
+ *  - DST4  : layer 4 (eg: TCP port) destination address in standard text form
+ *
+ * This line MUST be at the beginning of the buffer and MUST NOT wrap.
+ *
+ * Once the data is fetched, the values are set in the session's field and data
+ * are removed from the buffer. The function returns zero if it needs to wait
+ * for more data (max: timeout_client), or 1 if it has finished and removed itself.
+ */
+int frontend_decode_proxy_request(struct session *s, struct buffer *req, int an_bit)
+{
+	char *line = req->data;
+	char *end = req->data + req->l;
+	int len;
+
+	DPRINTF(stderr,"[%u] %s: session=%p b=%p, exp(r,w)=%u,%u bf=%08x bl=%d analysers=%02x\n",
+		now_ms, __FUNCTION__,
+		s,
+		req,
+		req->rex, req->wex,
+		req->flags,
+		req->l,
+		req->analysers);
+
+	if (req->flags & (BF_READ_ERROR|BF_READ_TIMEOUT))
+		goto fail;
+
+	len = MIN(req->l, 6);
+	if (!len)
+		goto missing;
+
+	/* Decode a possible proxy request, fail early if it does not match */
+	if (strncmp(line, "PROXY ", len) != 0)
+		goto fail;
+
+	line += 6;
+	if (req->l < 18) /* shortest possible line */
+		goto missing;
+
+	if (!memcmp(line, "TCP4 ", 5) != 0) {
+		u32 src3, dst3, sport, dport;
+
+		line += 5;
+
+		src3 = inetaddr_host_lim_ret(line, end, &line);
+		if (line == end)
+			goto missing;
+		if (*line++ != ' ')
+			goto fail;
+
+		dst3 = inetaddr_host_lim_ret(line, end, &line);
+		if (line == end)
+			goto missing;
+		if (*line++ != ' ')
+			goto fail;
+
+		sport = read_uint((const char **)&line, end);
+		if (line == end)
+			goto missing;
+		if (*line++ != ' ')
+			goto fail;
+
+		dport = read_uint((const char **)&line, end);
+		if (line > end - 2)
+			goto missing;
+		if (*line++ != '\r')
+			goto fail;
+		if (*line++ != '\n')
+			goto fail;
+
+		/* update the session's addresses and mark them set */
+		((struct sockaddr_in *)&s->cli_addr)->sin_family      = AF_INET;
+		((struct sockaddr_in *)&s->cli_addr)->sin_addr.s_addr = htonl(src3);
+		((struct sockaddr_in *)&s->cli_addr)->sin_port        = htons(sport);
+
+		((struct sockaddr_in *)&s->frt_addr)->sin_family      = AF_INET;
+		((struct sockaddr_in *)&s->frt_addr)->sin_addr.s_addr = htonl(dst3);
+		((struct sockaddr_in *)&s->frt_addr)->sin_port        = htons(dport);
+		s->flags |= SN_FRT_ADDR_SET;
+
+	}
+	else if (!memcmp(line, "TCP6 ", 5) != 0) {
+		u32 sport, dport;
+		char *src_s;
+		char *dst_s, *sport_s, *dport_s;
+		struct in6_addr src3, dst3;
+
+		line+=5;
+
+		src_s = line;
+		dst_s = sport_s = dport_s = NULL;
+		while (1) {
+			if (line > end - 2) {
+				goto missing;
+			}
+			else if (*line == '\r') {
+				*line = 0;
+				line++;
+				if (*line++ != '\n')
+					goto fail;
+				break;
+			}
+
+			if (*line == ' ') {
+				*line = 0;
+				if (!dst_s)
+					dst_s = line+1;
+				else if (!sport_s)
+					sport_s = line+1;
+				else if (!dport_s)
+					dport_s = line+1;
+			}
+			line++;
+		}
+
+		if (!dst_s || !sport_s || !dport_s)
+			goto fail;
+
+		sport = read_uint((const char **)&sport_s,dport_s-1);
+		if ( *sport_s != 0 )
+			goto fail;
+
+		dport = read_uint((const char **)&dport_s,line-2);
+		if ( *dport_s != 0 )
+			goto fail;
+
+		if (inet_pton(AF_INET6, src_s, (void *)&src3) != 1)
+			goto fail;
+
+		if (inet_pton(AF_INET6, dst_s, (void *)&dst3) != 1)
+			goto fail;
+
+		/* update the session's addresses and mark them set */
+		((struct sockaddr_in6 *)&s->cli_addr)->sin6_family      = AF_INET6;
+		memcpy(&((struct sockaddr_in6 *)&s->cli_addr)->sin6_addr, &src3, sizeof(struct in6_addr));
+		((struct sockaddr_in6 *)&s->cli_addr)->sin6_port        = htons(sport);
+
+		((struct sockaddr_in6 *)&s->frt_addr)->sin6_family      = AF_INET6;
+		memcpy(&((struct sockaddr_in6 *)&s->frt_addr)->sin6_addr, &dst3, sizeof(struct in6_addr));
+		((struct sockaddr_in6 *)&s->frt_addr)->sin6_port        = htons(dport);
+		s->flags |= SN_FRT_ADDR_SET;
+	}
+	else {
+		goto fail;
+	}
+
+	/* remove the PROXY line from the request */
+	len = line - req->data;
+	buffer_replace2(req, req->data, line, NULL, 0);
+	req->total -= len; /* don't count the header line */
+
+	req->analysers &= ~an_bit;
+	return 1;
+
+ missing:
+	if (!(req->flags & (BF_SHUTR|BF_FULL))) {
+		buffer_dont_connect(s->req);
+		return 0;
+	}
+	/* missing data and buffer is either full or shutdown => fail */
+
+ fail:
+	buffer_abort(req);
+	buffer_abort(s->rep);
+	req->analysers = 0;
+
+	s->fe->counters.failed_req++;
+	if (s->listener->counters)
+		s->listener->counters->failed_req++;
+
+	if (!(s->flags & SN_ERR_MASK))
+		s->flags |= SN_ERR_PRXCOND;
+	if (!(s->flags & SN_FINST_MASK))
+		s->flags |= SN_FINST_R;
+	return 0;
+}
+
 /* Retrieves the original destination address used by the client, and sets the
  * SN_FRT_ADDR_SET flag.
  */
Index: haproxy-1.4.25/src/proto_http.c
===================================================================
--- haproxy-1.4.25.orig/src/proto_http.c
+++ haproxy-1.4.25/src/proto_http.c
@@ -4209,7 +4209,8 @@ void http_end_txn_clean_session(struct s
 	if (s->rep->lr >= s->rep->data + s->rep->size)
 		s->rep->lr -= s->req->size;
 
-	s->req->analysers |= s->fe->fe_req_ana;
+	s->req->analysers = s->fe->fe_req_ana;
+	s->req->analysers &= ~AN_REQ_DECODE_PROXY;
 	s->rep->analysers = 0;
 
 	http_silent_debug(__LINE__, s);
@@ -7807,7 +7808,6 @@ void http_reset_txn(struct session *s)
 	http_init_txn(s);
 
 	s->be = s->fe;
-	s->req->analysers = s->listener->analysers;
 	s->logs.logwait = s->fe->to_log;
 	s->srv = s->prev_srv = s->srv_conn = NULL;
 	/* re-init store persistence */
Index: haproxy-1.4.25/src/session.c
===================================================================
--- haproxy-1.4.25.orig/src/session.c
+++ haproxy-1.4.25/src/session.c
@@ -34,6 +34,7 @@
 #include <proto/proxy.h>
 #include <proto/queue.h>
 #include <proto/server.h>
+#include <proto/client.h>
 #include <proto/stick_table.h>
 #include <proto/stream_interface.h>
 #include <proto/stream_sock.h>
@@ -1083,6 +1084,12 @@ resync_stream_interface:
 			while (ana_list && max_loops--) {
 				/* Warning! ensure that analysers are always placed in ascending order! */
 
+				if (ana_list & AN_REQ_DECODE_PROXY) {
+					if (!frontend_decode_proxy_request(s, s->req, AN_REQ_DECODE_PROXY))
+						break;
+					UPDATE_ANALYSERS(s->req->analysers, ana_list, ana_back, AN_REQ_DECODE_PROXY);
+				}
+
 				if (ana_list & AN_REQ_INSPECT) {
 					if (!tcp_inspect_request(s, s->req, AN_REQ_INSPECT))
 						break;
Index: haproxy-1.4.25/src/standard.c
===================================================================
--- haproxy-1.4.25.orig/src/standard.c
+++ haproxy-1.4.25/src/standard.c
@@ -569,6 +569,11 @@ unsigned int strl2uic(const char *s, int
 	return __strl2uic(s, len);
 }
 
+unsigned int read_uint(const char **s, const char *end)
+{
+	return __read_uint(s, end);
+}
+
 /* This one is 7 times faster than strtol() on athlon with checks.
  * It returns the value of the number composed of all valid digits read,
  * and can process negative numbers too.
@@ -993,12 +998,12 @@ unsigned int inetaddr_host_lim(const cha
  * Idem except the pointer to first unparsed byte is returned into <ret> which
  * must not be NULL.
  */
-unsigned int inetaddr_host_lim_ret(const char *text, char *stop, const char **ret)
+unsigned int inetaddr_host_lim_ret(char *text, char *stop, char **ret)
 {
 	const unsigned int ascii_zero = ('0' << 24) | ('0' << 16) | ('0' << 8) | '0';
 	register unsigned int dig100, dig10, dig1;
 	int s;
-	const char *p, *d;
+	char *p, *d;
 
 	dig1 = dig10 = dig100 = ascii_zero;
 	s = 24;