mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-08-06 23:27:04 +02:00
[MEDIUM] add support for RDP cookie persistence
The new statement "persist rdp-cookie" enables RDP cookie persistence. The RDP cookie is then extracted from the RDP protocol, and compared against available servers. If a server matches the RDP cookie, then it gets the connection.
This commit is contained in:
parent
736aa238a3
commit
647caf1ebc
@ -712,6 +712,7 @@ option tcpka X X X X
|
||||
option tcplog X X X X
|
||||
[no] option tcpsplice X X X X
|
||||
[no] option transparent X - X X
|
||||
persist rdp-cookie X - X X
|
||||
rate-limit sessions X X X -
|
||||
redirect - X X X
|
||||
redisp X - X X (deprecated)
|
||||
@ -2944,6 +2945,46 @@ no option transparent
|
||||
"transparent" option of the "bind" keyword.
|
||||
|
||||
|
||||
persist rdp-cookie
|
||||
persist rdp-cookie(name)
|
||||
Enable RDP cookie-based persistence
|
||||
May be used in sections : defaults | frontend | listen | backend
|
||||
yes | no | yes | yes
|
||||
Arguments :
|
||||
<name> is the optional name of the RDP cookie to check. If omitted, the
|
||||
default cookie name "mstshash" will be used. There currently is
|
||||
no valid reason to change this name.
|
||||
|
||||
This statement enables persistence based on an RDP cookie. The RDP cookie
|
||||
contains all information required to find the server in the list of known
|
||||
servers. So when this option is set in the backend, the request is analysed
|
||||
and if an RDP cookie is found, it is decoded. If it matches a known server
|
||||
which is still UP (or if "option persist" is set), then the connection is
|
||||
forwarded to this server.
|
||||
|
||||
Note that this only makes sense in a TCP backend, but for this to work, the
|
||||
frontend must have waited long enough to ensure that an RDP cookie is present
|
||||
in the request buffer. This is the same requirement as with the "rdp-cookie"
|
||||
load-balancing method. Thus it is higly recommended to put all statements in
|
||||
a single "listen" section.
|
||||
|
||||
Example :
|
||||
listen tse-farm
|
||||
bind :3389
|
||||
# wait up to 5s for an RDP cookie in the request
|
||||
tcp-request inspect-delay 5s
|
||||
tcp-request content accept if RDP_COOKIE
|
||||
# apply RDP cookie persistence
|
||||
persist rdp-cookie
|
||||
# if server is unknown, let's balance on the same cookie.
|
||||
# alternatively, "balance leastconn" may be useful too.
|
||||
balance rdp-cookie
|
||||
server srv1 1.1.1.1:3389
|
||||
server srv2 1.1.1.2:3389
|
||||
|
||||
See also : "balance rdp-cookie", "tcp-request" and the "req_rdp_cookie" ACL.
|
||||
|
||||
|
||||
rate-limit sessions <rate>
|
||||
Set a limit on the number of new sessions accepted per second on a frontend
|
||||
May be used in sections : defaults | frontend | listen | backend
|
||||
|
@ -35,6 +35,7 @@ int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen);
|
||||
int tcp_inspect_request(struct session *s, struct buffer *req, int an_bit);
|
||||
int acl_fetch_rdp_cookie(struct proxy *px, struct session *l4, void *l7, int dir,
|
||||
struct acl_expr *expr, struct acl_test *test);
|
||||
int tcp_persist_rdp_cookie(struct session *s, struct buffer *req, int an_bit);
|
||||
|
||||
#endif /* _PROTO_PROTO_TCP_H */
|
||||
|
||||
|
@ -116,6 +116,7 @@
|
||||
#define AN_REQ_UNIX_STATS 0x00000100 /* process unix stats socket request */
|
||||
|
||||
#define AN_RTR_HTTP_HDR 0x00000200 /* inspect HTTP response headers */
|
||||
#define AN_REQ_PRST_RDP_COOKIE 0x00000400 /* persistence on rdp cookie */
|
||||
|
||||
/* describes a chunk of string */
|
||||
struct chunk {
|
||||
|
@ -119,6 +119,7 @@
|
||||
#define PR_O2_LOGERRORS 0x00000040 /* log errors and retries at level LOG_ERR */
|
||||
#define PR_O2_SMARTACC 0x00000080 /* don't immediately ACK request after accept */
|
||||
#define PR_O2_SMARTCON 0x00000100 /* don't immediately send empty ACK after connect */
|
||||
#define PR_O2_RDPC_PRST 0x00000200 /* Actvate rdp cookie analyser */
|
||||
|
||||
/* This structure is used to apply fast weighted round robin on a server group */
|
||||
struct fwrr_group {
|
||||
@ -197,6 +198,8 @@ struct proxy {
|
||||
char *cookie_domain; /* domain used to insert the cookie */
|
||||
char *cookie_name; /* name of the cookie to look for */
|
||||
int cookie_len; /* strlen(cookie_name), computed only once */
|
||||
char *rdp_cookie_name; /* name of the RDP cookie to look for */
|
||||
int rdp_cookie_len; /* strlen(rdp_cookie_name), computed only once */
|
||||
char *url_param_name; /* name of the URL parameter used for hashing */
|
||||
int url_param_len; /* strlen(url_param_name), computed only once */
|
||||
unsigned url_param_post_limit; /* if checking POST body for URI parameter, max body to wait for */
|
||||
|
@ -824,6 +824,10 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
||||
curproxy->cookie_name = strdup(defproxy.cookie_name);
|
||||
curproxy->cookie_len = defproxy.cookie_len;
|
||||
|
||||
if (defproxy.rdp_cookie_name)
|
||||
curproxy->rdp_cookie_name = strdup(defproxy.rdp_cookie_name);
|
||||
curproxy->rdp_cookie_len = defproxy.rdp_cookie_len;
|
||||
|
||||
if (defproxy.url_param_name)
|
||||
curproxy->url_param_name = strdup(defproxy.url_param_name);
|
||||
curproxy->url_param_len = defproxy.url_param_len;
|
||||
@ -891,6 +895,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
||||
*/
|
||||
free(defproxy.check_req);
|
||||
free(defproxy.cookie_name);
|
||||
free(defproxy.rdp_cookie_name);
|
||||
free(defproxy.url_param_name);
|
||||
free(defproxy.hh_name);
|
||||
free(defproxy.capture_name);
|
||||
@ -1222,6 +1227,49 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
||||
return -1;
|
||||
}
|
||||
}/* end else if (!strcmp(args[0], "cookie")) */
|
||||
else if (!strcmp(args[0], "persist")) { /* persist */
|
||||
if (*(args[1]) == 0) {
|
||||
Alert("parsing [%s:%d] : missing persist method.\n",
|
||||
file, linenum);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!strncmp(args[1], "rdp-cookie", 10)) {
|
||||
curproxy->options2 |= PR_O2_RDPC_PRST;
|
||||
|
||||
if (*(args[1] + 10 ) == '(') { /* cookie name */
|
||||
const char *beg, *end;
|
||||
|
||||
beg = args[1] + 11;
|
||||
end = strchr(beg, ')');
|
||||
|
||||
if (!end || end == beg) {
|
||||
Alert("parsing [%s:%d] : persist rdp-cookie(name)' requires an rdp cookie name.\n",
|
||||
file, linenum);
|
||||
return -1;
|
||||
}
|
||||
|
||||
free(curproxy->rdp_cookie_name);
|
||||
curproxy->rdp_cookie_name = my_strndup(beg, end - beg);
|
||||
curproxy->rdp_cookie_len = end-beg;
|
||||
}
|
||||
else if (*(args[1] + 10 ) == '\0') { /* default cookie name 'msts' */
|
||||
free(curproxy->rdp_cookie_name);
|
||||
curproxy->rdp_cookie_name = strdup("msts");
|
||||
curproxy->rdp_cookie_len = strlen(curproxy->rdp_cookie_name);
|
||||
}
|
||||
else { /* syntax */
|
||||
Alert("parsing [%s:%d] : persist rdp-cookie(name)' requires an rdp cookie name.\n",
|
||||
file, linenum);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Alert("parsing [%s:%d] : unknown persist method.\n",
|
||||
file, linenum);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if (!strcmp(args[0], "appsession")) { /* cookie name */
|
||||
|
||||
if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[0], NULL))
|
||||
|
@ -446,6 +446,74 @@ int tcp_inspect_request(struct session *s, struct buffer *req, int an_bit)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Apply RDP cookie persistence to the current session. For this, the function
|
||||
* tries to extract an RDP cookie from the request buffer, and look for the
|
||||
* matching server in the list. If the server is found, it is assigned to the
|
||||
* session. This always returns 1, and the analyser removes itself from the
|
||||
* list. Nothing is performed if a server was already assigned.
|
||||
*/
|
||||
int tcp_persist_rdp_cookie(struct session *s, struct buffer *req, int an_bit)
|
||||
{
|
||||
struct proxy *px = s->be;
|
||||
int ret;
|
||||
struct acl_expr expr;
|
||||
struct acl_test test;
|
||||
struct server *srv = px->srv;
|
||||
struct sockaddr_in addr;
|
||||
char *p;
|
||||
|
||||
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 (s->flags & SN_ASSIGNED)
|
||||
goto no_cookie;
|
||||
|
||||
memset(&expr, 0, sizeof(expr));
|
||||
memset(&test, 0, sizeof(test));
|
||||
|
||||
expr.arg.str = s->be->rdp_cookie_name;
|
||||
expr.arg_len = s->be->rdp_cookie_len;
|
||||
|
||||
ret = acl_fetch_rdp_cookie(px, s, NULL, ACL_DIR_REQ, &expr, &test);
|
||||
if (ret == 0 || (test.flags & ACL_TEST_F_MAY_CHANGE) || test.len == 0)
|
||||
goto no_cookie;
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
|
||||
/* Considering an rdp cookie detected using acl, test.ptr ended with <cr><lf> and should return */
|
||||
addr.sin_addr.s_addr = strtoul(test.ptr, &p, 10);
|
||||
if (*p != '.')
|
||||
goto no_cookie;
|
||||
p++;
|
||||
addr.sin_port = (unsigned short)strtoul(p, &p, 10);
|
||||
if (*p != '.')
|
||||
goto no_cookie;
|
||||
|
||||
while (srv) {
|
||||
if (memcmp(&addr, &(srv->addr), sizeof(addr)) == 0) {
|
||||
if ((srv->state & SRV_RUNNING) || (px->options & PR_O_PERSIST)) {
|
||||
/* we found the server and it is usable */
|
||||
s->flags |= SN_DIRECT | SN_ASSIGNED;
|
||||
s->srv = srv;
|
||||
break;
|
||||
}
|
||||
}
|
||||
srv = srv->next;
|
||||
}
|
||||
|
||||
no_cookie:
|
||||
req->analysers &= ~an_bit;
|
||||
req->analyse_exp = TICK_ETERNITY;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* This function should be called to parse a line starting with the "tcp-request"
|
||||
* keyword.
|
||||
|
@ -677,6 +677,12 @@ int session_set_backend(struct session *s, struct proxy *be)
|
||||
s->req->analysers |= AN_REQ_WAIT_HTTP | AN_REQ_HTTP_PROCESS_BE | AN_REQ_HTTP_INNER;
|
||||
}
|
||||
|
||||
/* If the backend does requires RDP cookie persistence, we have to
|
||||
* enable the corresponding analyser.
|
||||
*/
|
||||
if (s->be->options2 & PR_O2_RDPC_PRST)
|
||||
s->req->analysers |= AN_REQ_PRST_RDP_COOKIE;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -850,6 +850,12 @@ struct task *process_session(struct task *t)
|
||||
if (!http_process_request_body(s, s->req, AN_REQ_HTTP_BODY))
|
||||
break;
|
||||
}
|
||||
|
||||
if (s->req->analysers & AN_REQ_PRST_RDP_COOKIE) {
|
||||
last_ana |= AN_REQ_PRST_RDP_COOKIE;
|
||||
if (!tcp_persist_rdp_cookie(s, s->req, AN_REQ_PRST_RDP_COOKIE))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user