diff --git a/gennghttpxfun.py b/gennghttpxfun.py index 50a0dd0f..bd7ccd9f 100755 --- a/gennghttpxfun.py +++ b/gennghttpxfun.py @@ -164,6 +164,8 @@ OPTIONS = [ "frontend-max-requests", "single-thread", "single-process", + "no-add-x-forwarded-proto", + "no-strip-incoming-x-forwarded-proto", ] LOGVARS = [ diff --git a/src/shrpx.cc b/src/shrpx.cc index 52a4477e..7601f1f3 100644 --- a/src/shrpx.cc +++ b/src/shrpx.cc @@ -1478,6 +1478,8 @@ void fill_default_config(Config *config) { httpconf.max_response_header_fields = 500; httpconf.redirect_https_port = StringRef::from_lit("443"); httpconf.max_requests = std::numeric_limits::max(); + httpconf.xfp.add = true; + httpconf.xfp.strip_incoming = true; auto &http2conf = config->http2; { @@ -2485,6 +2487,15 @@ HTTP: --strip-incoming-x-forwarded-for Strip X-Forwarded-For header field from inbound client requests. + --no-add-x-forwarded-proto + Don't append additional X-Forwarded-Proto header field + to the backend request. If inbound client sets + X-Forwarded-Proto, and + --no-strip-incoming-x-forwarded-proto option is used, + they are passed to the backend. + --no-strip-incoming-x-forwarded-proto + Don't strip X-Forwarded-Proto header field from inbound + client requests. --add-forwarded= Append RFC 7239 Forwarded header field with parameters specified in comma delimited list . The supported @@ -3327,6 +3338,9 @@ int main(int argc, char **argv) { {SHRPX_OPT_FRONTEND_MAX_REQUESTS.c_str(), required_argument, &flag, 155}, {SHRPX_OPT_SINGLE_THREAD.c_str(), no_argument, &flag, 156}, + {SHRPX_OPT_NO_ADD_X_FORWARDED_PROTO.c_str(), no_argument, &flag, 157}, + {SHRPX_OPT_NO_STRIP_INCOMING_X_FORWARDED_PROTO.c_str(), no_argument, + &flag, 158}, {SHRPX_OPT_SINGLE_PROCESS.c_str(), no_argument, &flag, 159}, {nullptr, 0, nullptr, 0}}; @@ -4064,6 +4078,16 @@ int main(int argc, char **argv) { cmdcfgs.emplace_back(SHRPX_OPT_SINGLE_THREAD, StringRef::from_lit("yes")); break; + case 157: + // --no-add-x-forwarded-proto + cmdcfgs.emplace_back(SHRPX_OPT_NO_ADD_X_FORWARDED_PROTO, + StringRef::from_lit("yes")); + break; + case 158: + // --no-strip-incoming-x-forwarded-proto + cmdcfgs.emplace_back(SHRPX_OPT_NO_STRIP_INCOMING_X_FORWARDED_PROTO, + StringRef::from_lit("yes")); + break; case 159: // --single-process cmdcfgs.emplace_back(SHRPX_OPT_SINGLE_PROCESS, diff --git a/src/shrpx_config.cc b/src/shrpx_config.cc index c5077345..34f6cc06 100644 --- a/src/shrpx_config.cc +++ b/src/shrpx_config.cc @@ -1905,6 +1905,11 @@ int option_lookup_token(const char *name, size_t namelen) { return SHRPX_OPTID_FETCH_OCSP_RESPONSE_FILE; } break; + case 'o': + if (util::strieq_l("no-add-x-forwarded-prot", name, 23)) { + return SHRPX_OPTID_NO_ADD_X_FORWARDED_PROTO; + } + break; case 't': if (util::strieq_l("listener-disable-timeou", name, 23)) { return SHRPX_OPTID_LISTENER_DISABLE_TIMEOUT; @@ -2101,6 +2106,11 @@ int option_lookup_token(const char *name, size_t namelen) { return SHRPX_OPTID_FRONTEND_HTTP2_OPTIMIZE_WINDOW_SIZE; } break; + case 'o': + if (util::strieq_l("no-strip-incoming-x-forwarded-prot", name, 34)) { + return SHRPX_OPTID_NO_STRIP_INCOMING_X_FORWARDED_PROTO; + } + break; case 'r': if (util::strieq_l("frontend-http2-dump-response-heade", name, 34)) { return SHRPX_OPTID_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER; @@ -3363,6 +3373,14 @@ int parse_config(Config *config, int optid, const StringRef &opt, case SHRPX_OPTID_SINGLE_PROCESS: config->single_process = util::strieq_l("yes", optarg); + return 0; + case SHRPX_OPTID_NO_ADD_X_FORWARDED_PROTO: + config->http.xfp.add = !util::strieq_l("yes", optarg); + + return 0; + case SHRPX_OPTID_NO_STRIP_INCOMING_X_FORWARDED_PROTO: + config->http.xfp.strip_incoming = !util::strieq_l("yes", optarg); + return 0; case SHRPX_OPTID_CONF: LOG(WARN) << "conf: ignored"; diff --git a/src/shrpx_config.h b/src/shrpx_config.h index ebd425a7..e0c81cd2 100644 --- a/src/shrpx_config.h +++ b/src/shrpx_config.h @@ -337,6 +337,10 @@ constexpr auto SHRPX_OPT_FRONTEND_MAX_REQUESTS = StringRef::from_lit("frontend-max-requests"); constexpr auto SHRPX_OPT_SINGLE_THREAD = StringRef::from_lit("single-thread"); constexpr auto SHRPX_OPT_SINGLE_PROCESS = StringRef::from_lit("single-process"); +constexpr auto SHRPX_OPT_NO_ADD_X_FORWARDED_PROTO = + StringRef::from_lit("no-add-x-forwarded-proto"); +constexpr auto SHRPX_OPT_NO_STRIP_INCOMING_X_FORWARDED_PROTO = + StringRef::from_lit("no-strip-incoming-x-forwarded-proto"); constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8; @@ -639,6 +643,10 @@ struct HttpConfig { bool add; bool strip_incoming; } xff; + struct { + bool add; + bool strip_incoming; + } xfp; std::vector altsvcs; std::vector error_pages; HeaderRefs add_request_headers; @@ -1023,6 +1031,7 @@ enum { SHRPX_OPTID_MAX_REQUEST_HEADER_FIELDS, SHRPX_OPTID_MAX_RESPONSE_HEADER_FIELDS, SHRPX_OPTID_MRUBY_FILE, + SHRPX_OPTID_NO_ADD_X_FORWARDED_PROTO, SHRPX_OPTID_NO_HOST_REWRITE, SHRPX_OPTID_NO_HTTP2_CIPHER_BLACK_LIST, SHRPX_OPTID_NO_KQUEUE, @@ -1030,6 +1039,7 @@ enum { SHRPX_OPTID_NO_OCSP, SHRPX_OPTID_NO_SERVER_PUSH, SHRPX_OPTID_NO_SERVER_REWRITE, + SHRPX_OPTID_NO_STRIP_INCOMING_X_FORWARDED_PROTO, SHRPX_OPTID_NO_VIA, SHRPX_OPTID_NPN_LIST, SHRPX_OPTID_OCSP_UPDATE_INTERVAL, diff --git a/src/shrpx_http2_downstream_connection.cc b/src/shrpx_http2_downstream_connection.cc index 019f3f9d..99856a5c 100644 --- a/src/shrpx_http2_downstream_connection.cc +++ b/src/shrpx_http2_downstream_connection.cc @@ -371,8 +371,24 @@ int Http2DownstreamConnection::push_request_headers() { } if (!config->http2_proxy && req.method != HTTP_CONNECT) { - // We use same protocol with :scheme header field - nva.push_back(http2::make_nv_ls_nocopy("x-forwarded-proto", req.scheme)); + auto &xfpconf = httpconf.xfp; + auto xfp = xfpconf.strip_incoming + ? nullptr + : req.fs.header(http2::HD_X_FORWARDED_PROTO); + + if (xfpconf.add) { + StringRef xfp_value; + // We use same protocol with :scheme header field + if (xfp) { + xfp_value = concat_string_ref(balloc, xfp->value, + StringRef::from_lit(", "), req.scheme); + } else { + xfp_value = req.scheme; + } + nva.push_back(http2::make_nv_ls_nocopy("x-forwarded-proto", xfp_value)); + } else if (xfp) { + nva.push_back(http2::make_nv_ls_nocopy("x-forwarded-proto", xfp->value)); + } } auto via = req.fs.header(http2::HD_VIA); diff --git a/src/shrpx_http_downstream_connection.cc b/src/shrpx_http_downstream_connection.cc index 21658560..8c82b3df 100644 --- a/src/shrpx_http_downstream_connection.cc +++ b/src/shrpx_http_downstream_connection.cc @@ -630,10 +630,25 @@ int HttpDownstreamConnection::push_request_headers() { buf->append("\r\n"); } if (!config->http2_proxy && !connect_method) { - buf->append("X-Forwarded-Proto: "); - assert(!req.scheme.empty()); - buf->append(req.scheme); - buf->append("\r\n"); + auto &xfpconf = httpconf.xfp; + auto xfp = xfpconf.strip_incoming + ? nullptr + : req.fs.header(http2::HD_X_FORWARDED_PROTO); + + if (xfpconf.add) { + buf->append("X-Forwarded-Proto: "); + if (xfp) { + buf->append((*xfp).value); + buf->append(", "); + } + assert(!req.scheme.empty()); + buf->append(req.scheme); + buf->append("\r\n"); + } else if (xfp) { + buf->append("X-Forwarded-Proto: "); + buf->append((*xfp).value); + buf->append("\r\n"); + } } auto via = req.fs.header(http2::HD_VIA); if (httpconf.no_via) {