From a2e2362ee9bcc1a4ccabae6be9a4d159f40d1681 Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Mon, 30 Sep 2024 19:11:50 +0100 Subject: [PATCH] add proxy-protocol header disable option (#9203) resolves https://github.com/neondatabase/cloud/issues/18026 --- proxy/src/bin/local_proxy.rs | 2 +- proxy/src/bin/proxy.rs | 11 +++++++---- proxy/src/config.rs | 13 ++++++++++++- proxy/src/proxy.rs | 11 ++++++++--- 4 files changed, 28 insertions(+), 9 deletions(-) diff --git a/proxy/src/bin/local_proxy.rs b/proxy/src/bin/local_proxy.rs index 1b3f465686..a7bdac910f 100644 --- a/proxy/src/bin/local_proxy.rs +++ b/proxy/src/bin/local_proxy.rs @@ -274,7 +274,7 @@ fn build_config(args: &LocalProxyCliArgs) -> anyhow::Result<&'static ProxyConfig rate_limit_ip_subnet: 64, ip_allowlist_check_enabled: true, }, - require_client_ip: false, + proxy_protocol_v2: config::ProxyProtocolV2::Rejected, handshake_timeout: Duration::from_secs(10), region: "local".into(), wake_compute_retry_config: RetryConfig::parse(RetryConfig::WAKE_COMPUTE_DEFAULT_VALUES)?, diff --git a/proxy/src/bin/proxy.rs b/proxy/src/bin/proxy.rs index 141005788d..50d204fca6 100644 --- a/proxy/src/bin/proxy.rs +++ b/proxy/src/bin/proxy.rs @@ -17,6 +17,7 @@ use proxy::config::AuthenticationConfig; use proxy::config::CacheOptions; use proxy::config::HttpConfig; use proxy::config::ProjectInfoCacheOptions; +use proxy::config::ProxyProtocolV2; use proxy::console; use proxy::context::parquet::ParquetUploadArgs; use proxy::http; @@ -144,9 +145,6 @@ struct ProxyCliArgs { /// size of the threadpool for password hashing #[clap(long, default_value_t = 4)] scram_thread_pool_size: u8, - /// Require that all incoming requests have a Proxy Protocol V2 packet **and** have an IP address associated. - #[clap(long, default_value_t = false, value_parser = clap::builder::BoolishValueParser::new(), action = clap::ArgAction::Set)] - require_client_ip: bool, /// Disable dynamic rate limiter and store the metrics to ensure its production behaviour. #[clap(long, default_value_t = true, value_parser = clap::builder::BoolishValueParser::new(), action = clap::ArgAction::Set)] disable_dynamic_rate_limiter: bool, @@ -229,6 +227,11 @@ struct ProxyCliArgs { /// Configure if this is a private access proxy for the POC: In that case the proxy will ignore the IP allowlist #[clap(long, default_value_t = false, value_parser = clap::builder::BoolishValueParser::new(), action = clap::ArgAction::Set)] is_private_access_proxy: bool, + + /// Configure whether all incoming requests have a Proxy Protocol V2 packet. + // TODO(conradludgate): switch default to rejected or required once we've updated all deployments + #[clap(value_enum, long, default_value_t = ProxyProtocolV2::Supported)] + proxy_protocol_v2: ProxyProtocolV2, } #[derive(clap::Args, Clone, Copy, Debug)] @@ -704,7 +707,7 @@ fn build_config(args: &ProxyCliArgs) -> anyhow::Result<&'static ProxyConfig> { allow_self_signed_compute: args.allow_self_signed_compute, http_config, authentication_config, - require_client_ip: args.require_client_ip, + proxy_protocol_v2: args.proxy_protocol_v2, handshake_timeout: args.handshake_timeout, region: args.region.clone(), wake_compute_retry_config: config::RetryConfig::parse(&args.wake_compute_retry)?, diff --git a/proxy/src/config.rs b/proxy/src/config.rs index 373e4cf650..a66d4773a3 100644 --- a/proxy/src/config.rs +++ b/proxy/src/config.rs @@ -7,6 +7,7 @@ use crate::{ Host, }; use anyhow::{bail, ensure, Context, Ok}; +use clap::ValueEnum; use itertools::Itertools; use remote_storage::RemoteStorageConfig; use rustls::{ @@ -30,7 +31,7 @@ pub struct ProxyConfig { pub allow_self_signed_compute: bool, pub http_config: HttpConfig, pub authentication_config: AuthenticationConfig, - pub require_client_ip: bool, + pub proxy_protocol_v2: ProxyProtocolV2, pub region: String, pub handshake_timeout: Duration, pub wake_compute_retry_config: RetryConfig, @@ -38,6 +39,16 @@ pub struct ProxyConfig { pub connect_to_compute_retry_config: RetryConfig, } +#[derive(Copy, Clone, Debug, ValueEnum, PartialEq)] +pub enum ProxyProtocolV2 { + /// Connection will error if PROXY protocol v2 header is missing + Required, + /// Connection will parse PROXY protocol v2 header, but accept the connection if it's missing. + Supported, + /// Connection will error if PROXY protocol v2 header is provided + Rejected, +} + #[derive(Debug)] pub struct MetricCollectionConfig { pub endpoint: reqwest::Url, diff --git a/proxy/src/proxy.rs b/proxy/src/proxy.rs index ff199ac701..7003af2aba 100644 --- a/proxy/src/proxy.rs +++ b/proxy/src/proxy.rs @@ -10,6 +10,7 @@ pub(crate) mod wake_compute; pub use copy_bidirectional::copy_bidirectional_client_compute; pub use copy_bidirectional::ErrorSource; +use crate::config::ProxyProtocolV2; use crate::{ auth, cancellation::{self, CancellationHandlerMain, CancellationHandlerMainInternal}, @@ -93,15 +94,19 @@ pub async fn task_main( connections.spawn(async move { let (socket, peer_addr) = match read_proxy_protocol(socket).await { - Ok((socket, Some(addr))) => (socket, addr.ip()), Err(e) => { error!("per-client task finished with an error: {e:#}"); return; } - Ok((_socket, None)) if config.require_client_ip => { - error!("missing required client IP"); + Ok((_socket, None)) if config.proxy_protocol_v2 == ProxyProtocolV2::Required => { + error!("missing required proxy protocol header"); return; } + Ok((_socket, Some(_))) if config.proxy_protocol_v2 == ProxyProtocolV2::Rejected => { + error!("proxy protocol header not supported"); + return; + } + Ok((socket, Some(addr))) => (socket, addr.ip()), Ok((socket, None)) => (socket, peer_addr.ip()), };