diff --git a/doc/configuration.txt b/doc/configuration.txt index dd418f409..90834a716 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -3672,6 +3672,22 @@ http-request { allow | deny | tarpit | auth [realm ] | redirect | http-request set-var(req.my_var) req.fhdr(user-agent),lower + - set-src : + Is used to set the source IP address to the value of specified + expression. Useful when a proxy in front of HAProxy rewrites source IP, + but provides the correct IP in a HTTP header; or you want to mask + source IP for privacy. + + Is a standard HAProxy expression formed by a sample-fetch + followed by some converters. + + Example: + + http-request set-src hdr(x-forwarded-for) + http-request set-src src,ipmask(24) + + When set-src is successful, the source port is set to 0. + There is no limit to the number of http-request statements per instance. It is important to know that http-request rules are processed very early in diff --git a/include/types/proto_http.h b/include/types/proto_http.h index fc8647593..9b14f268f 100644 --- a/include/types/proto_http.h +++ b/include/types/proto_http.h @@ -262,6 +262,7 @@ enum { HTTP_REQ_ACT_SET_MAP, HTTP_REQ_ACT_CUSTOM_STOP, HTTP_REQ_ACT_CUSTOM_CONT, + HTTP_REQ_ACT_SET_SRC, HTTP_REQ_ACT_TRK_SC0, /* SC1, SC2, ... SCn */ HTTP_REQ_ACT_TRK_SCMAX = HTTP_REQ_ACT_TRK_SC0 + MAX_SESS_STKCTR - 1, diff --git a/src/proto_http.c b/src/proto_http.c index c24479240..0ebc2c409 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -3674,6 +3674,27 @@ resume_execution: stkctr_set_flags(&s->stkctr[http_req_trk_idx(rule->action)], STKCTR_TRACK_BACKEND); } } + break; + + case HTTP_REQ_ACT_SET_SRC: + if ((cli_conn = objt_conn(sess->origin)) && conn_ctrl_ready(cli_conn)) { + struct sample *smp; + + smp = sample_fetch_as_type(px, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.act.p[0], SMP_T_ADDR); + + if (smp) { + if (smp->type == SMP_T_IPV4) { + ((struct sockaddr_in *)&cli_conn->addr.from)->sin_family = AF_INET; + ((struct sockaddr_in *)&cli_conn->addr.from)->sin_addr.s_addr = smp->data.ipv4.s_addr; + ((struct sockaddr_in *)&cli_conn->addr.from)->sin_port = 0; + } else if (smp->type == SMP_T_IPV6) { + ((struct sockaddr_in6 *)&cli_conn->addr.from)->sin6_family = AF_INET6; + memcpy(&((struct sockaddr_in6 *)&cli_conn->addr.from)->sin6_addr, &smp->data.ipv6, sizeof(struct in6_addr)); + ((struct sockaddr_in6 *)&cli_conn->addr.from)->sin6_port = 0; + } + } + } + break; } } @@ -9528,6 +9549,39 @@ struct http_req_rule *parse_http_req_cond(const char **args, const char *file, i proxy->conf.lfs_line = proxy->conf.args.line; cur_arg += 2; + } else if (strncmp(args[0], "set-src", 7) == 0) { + struct sample_expr *expr; + unsigned int where; + char *err = NULL; + + cur_arg = 1; + proxy->conf.args.ctx = ARGC_HRQ; + + expr = sample_parse_expr((char **)args, &cur_arg, file, linenum, &err, &proxy->conf.args); + if (!expr) { + Alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-request %s' rule : %s.\n", + file, linenum, proxy_type_str(proxy), proxy->id, args[0], err); + free(err); + goto out_err; + } + + where = 0; + if (proxy->cap & PR_CAP_FE) + where |= SMP_VAL_FE_HRQ_HDR; + if (proxy->cap & PR_CAP_BE) + where |= SMP_VAL_BE_HRQ_HDR; + + if (!(expr->fetch->val & where)) { + Alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-request %s' rule :" + " fetch method '%s' extracts information from '%s', none of which is available here.\n", + file, linenum, proxy_type_str(proxy), proxy->id, args[0], + args[cur_arg-1], sample_src_names(expr->fetch->use)); + free(expr); + goto out_err; + } + + rule->arg.act.p[0] = expr; + rule->action = HTTP_REQ_ACT_SET_SRC; } else if (((custom = action_http_req_custom(args[0])) != NULL)) { char *errmsg = NULL; cur_arg = 1; @@ -9539,7 +9593,7 @@ struct http_req_rule *parse_http_req_cond(const char **args, const char *file, i goto out_err; } } else { - Alert("parsing [%s:%d]: 'http-request' expects 'allow', 'deny', 'auth', 'redirect', 'tarpit', 'add-header', 'set-header', 'replace-header', 'replace-value', 'set-nice', 'set-tos', 'set-mark', 'set-log-level', 'add-acl', 'del-acl', 'del-map', 'set-map', 'set-var', but got '%s'%s.\n", + Alert("parsing [%s:%d]: 'http-request' expects 'allow', 'deny', 'auth', 'redirect', 'tarpit', 'add-header', 'set-header', 'replace-header', 'replace-value', 'set-nice', 'set-tos', 'set-mark', 'set-log-level', 'add-acl', 'del-acl', 'del-map', 'set-map', 'set-var', 'set-src', but got '%s'%s.\n", file, linenum, args[0], *args[0] ? "" : " (missing argument)"); goto out_err; }