* [pve-devel] [PATCH SERIES manager/http-server/docs] fix #5699: add support for real IP @ 2024-09-10 0:30 Thomas Skinner 2024-09-10 0:30 ` [pve-devel] [PATCH docs 1/1] fix #5699: pveproxy: add docs for real IP support Thomas Skinner ` (2 more replies) 0 siblings, 3 replies; 12+ messages in thread From: Thomas Skinner @ 2024-09-10 0:30 UTC (permalink / raw) To: pve-devel This patch series adds support for setting a HTTP header for passing through a client IP address for logging purposes. Two settings are introduced for /etc/default/pveproxy: PROXY_REAL_IP_HEADER: defines a HTTP header to extract a client IP address from in the request. If the IP address is invalid, it is ignored. Otherwise, it is logged in addition to the sending peer IP address. TRUSTED_PROXY_IPS: defines a list of IP addresses or ranges that are allowed to set the header defined in PROXY_REAL_IP_HEADER. If this setting is not set, any requests with the PROXY_REAL_IP_HEADER set will have the extracted IP address logged. pve-docs: Thomas Skinner (1): fix #5699: pveproxy: add docs for real IP support pveproxy.adoc | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) pve-http-server: Thomas Skinner (1): fix #5699: pveproxy: add library methods for real IP support src/PVE/APIServer/AnyEvent.pm | 43 ++++++++++++++++++++++++++++++++--- src/PVE/APIServer/Utils.pm | 15 ++++++++++++ 2 files changed, 55 insertions(+), 3 deletions(-) pve-manager: Thomas Skinner (1): fix #5699: pveproxy: add settings for real IP support PVE/Service/pveproxy.pm | 2 ++ 1 file changed, 2 insertions(+) -- 2.39.2 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel ^ permalink raw reply [flat|nested] 12+ messages in thread
* [pve-devel] [PATCH docs 1/1] fix #5699: pveproxy: add docs for real IP support 2024-09-10 0:30 [pve-devel] [PATCH SERIES manager/http-server/docs] fix #5699: add support for real IP Thomas Skinner @ 2024-09-10 0:30 ` Thomas Skinner 2024-11-13 11:36 ` Fabian Grünbichler 2024-09-10 0:30 ` [pve-devel] [PATCH http-server 1/1] fix #5699: pveproxy: add library methods " Thomas Skinner 2024-09-10 0:30 ` [pve-devel] [PATCH manager 1/1] fix #5699: pveproxy: add settings " Thomas Skinner 2 siblings, 1 reply; 12+ messages in thread From: Thomas Skinner @ 2024-09-10 0:30 UTC (permalink / raw) To: pve-devel --- pveproxy.adoc | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/pveproxy.adoc b/pveproxy.adoc index 4b5dac0..f0ae0f7 100644 --- a/pveproxy.adoc +++ b/pveproxy.adoc @@ -198,6 +198,35 @@ content, if the client supports it. This can disabled in `/etc/default/pveproxy` COMPRESSION=0 +[[pveproxy_real_ip]] +Real Client IP Logging +---------------------- + +By default, `pveproxy` logs the IP address of the client that sent the request. +In cases where a proxy server is in front of `pveproxy`, it may be desirable to +log the IP of the client making the request instead of the proxy IP. + +To enable processing of a HTTP header set by the proxy for logging purposes, set +`PROXY_REAL_IP_HEADER` to the name of the header to retrieve the client IP from. For +example: + + PROXY_REAL_IP_HEADER="X-Forwarded-For" + +Any invalid values passed in this header will be ignored. + +The default behavior is log the value in this header on all incoming requests. +To define a list of proxy servers that should be trusted to set the above HTTP +header, set `TRUSTED_PROXY_IPS`, for example: + + TRUSTED_PROXY_IPS="192.168.0.2" + +The `TRUSTED_PROXY_IPS` setting also supports values similar to the `ALLOW_FROM` +and `DENY_FROM` settings. + +IP addresses can be specified using any syntax understood by `Net::IP`. The +name `all` is an alias for `0/0` and `::/0` (meaning all IPv4 and IPv6 +addresses). + ifdef::manvolnum[] include::pve-copyright.adoc[] endif::manvolnum[] -- 2.39.2 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [pve-devel] [PATCH docs 1/1] fix #5699: pveproxy: add docs for real IP support 2024-09-10 0:30 ` [pve-devel] [PATCH docs 1/1] fix #5699: pveproxy: add docs for real IP support Thomas Skinner @ 2024-11-13 11:36 ` Fabian Grünbichler 0 siblings, 0 replies; 12+ messages in thread From: Fabian Grünbichler @ 2024-11-13 11:36 UTC (permalink / raw) To: Proxmox VE development discussion; +Cc: Thomas Skinner thanks for sending docs along with your feature patch! this one looks good to me, but see my comments on the feature patch itself. On September 10, 2024 2:30 am, Thomas Skinner wrote: > --- > pveproxy.adoc | 29 +++++++++++++++++++++++++++++ > 1 file changed, 29 insertions(+) > > diff --git a/pveproxy.adoc b/pveproxy.adoc > index 4b5dac0..f0ae0f7 100644 > --- a/pveproxy.adoc > +++ b/pveproxy.adoc > @@ -198,6 +198,35 @@ content, if the client supports it. This can disabled in `/etc/default/pveproxy` > > COMPRESSION=0 > > +[[pveproxy_real_ip]] > +Real Client IP Logging > +---------------------- > + > +By default, `pveproxy` logs the IP address of the client that sent the request. > +In cases where a proxy server is in front of `pveproxy`, it may be desirable to > +log the IP of the client making the request instead of the proxy IP. > + > +To enable processing of a HTTP header set by the proxy for logging purposes, set > +`PROXY_REAL_IP_HEADER` to the name of the header to retrieve the client IP from. For > +example: > + > + PROXY_REAL_IP_HEADER="X-Forwarded-For" > + > +Any invalid values passed in this header will be ignored. > + > +The default behavior is log the value in this header on all incoming requests. > +To define a list of proxy servers that should be trusted to set the above HTTP > +header, set `TRUSTED_PROXY_IPS`, for example: > + > + TRUSTED_PROXY_IPS="192.168.0.2" > + > +The `TRUSTED_PROXY_IPS` setting also supports values similar to the `ALLOW_FROM` > +and `DENY_FROM` settings. > + > +IP addresses can be specified using any syntax understood by `Net::IP`. The > +name `all` is an alias for `0/0` and `::/0` (meaning all IPv4 and IPv6 > +addresses). > + > ifdef::manvolnum[] > include::pve-copyright.adoc[] > endif::manvolnum[] > -- > 2.39.2 > > > _______________________________________________ > pve-devel mailing list > pve-devel@lists.proxmox.com > https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel > > > _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel ^ permalink raw reply [flat|nested] 12+ messages in thread
* [pve-devel] [PATCH http-server 1/1] fix #5699: pveproxy: add library methods for real IP support 2024-09-10 0:30 [pve-devel] [PATCH SERIES manager/http-server/docs] fix #5699: add support for real IP Thomas Skinner 2024-09-10 0:30 ` [pve-devel] [PATCH docs 1/1] fix #5699: pveproxy: add docs for real IP support Thomas Skinner @ 2024-09-10 0:30 ` Thomas Skinner 2024-11-13 11:34 ` Fabian Grünbichler 2024-09-10 0:30 ` [pve-devel] [PATCH manager 1/1] fix #5699: pveproxy: add settings " Thomas Skinner 2 siblings, 1 reply; 12+ messages in thread From: Thomas Skinner @ 2024-09-10 0:30 UTC (permalink / raw) To: pve-devel --- src/PVE/APIServer/AnyEvent.pm | 43 ++++++++++++++++++++++++++++++++--- src/PVE/APIServer/Utils.pm | 15 ++++++++++++ 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/src/PVE/APIServer/AnyEvent.pm b/src/PVE/APIServer/AnyEvent.pm index a8d60c1..c2afb4d 100644 --- a/src/PVE/APIServer/AnyEvent.pm +++ b/src/PVE/APIServer/AnyEvent.pm @@ -85,20 +85,21 @@ sub log_request { my $loginfo = $reqstate->{log}; - # like apache2 common log format - # LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" + # like apache2 common log format + client IP address + # LogFormat "%a %h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" return if $loginfo->{written}; # avoid duplicate logs $loginfo->{written} = 1; my $peerip = $reqstate->{peer_host} || '-'; + my $realip = $loginfo->{real_ip} || $peerip; my $userid = $loginfo->{userid} || '-'; my $content_length = defined($loginfo->{content_length}) ? $loginfo->{content_length} : '-'; my $code = $loginfo->{code} || 500; my $requestline = $loginfo->{requestline} || '-'; my $timestr = strftime("%d/%m/%Y:%H:%M:%S %z", localtime()); - my $msg = "$peerip - $userid [$timestr] \"$requestline\" $code $content_length\n"; + my $msg = "$realip $peerip - $userid [$timestr] \"$requestline\" $code $content_length\n"; $self->write_log($msg); } @@ -1462,6 +1463,14 @@ sub authenticate_and_handle_request { my $auth = {}; + if ($self->{proxy_real_ip_header} && $request->header($self->{proxy_real_ip_header})) { + my $real_ip = Net::IP->new($request->header($self->{proxy_real_ip_header})) || undef; + $reqstate->{log}->{real_ip} = Net::IP::ip_compress_address( + $real_ip->ip(), + $real_ip->version(), + ) if defined($real_ip) && $self->check_trusted_proxy($reqstate->{peer_host}); + } + if ($self->{spiceproxy}) { my $connect_str = $request->header('Host'); my ($vmid, $node, $port) = $self->verify_spice_connect_url($connect_str); @@ -1801,6 +1810,34 @@ sub check_host_access { return $match_allow; } +sub check_trusted_proxy { + my ($self, $clientip) = @_; + + $clientip = PVE::APIServer::Utils::normalize_v4_in_v6($clientip); + my $cip = Net::IP->new($clientip); + + if (!$cip) { + $self->dprint("client IP not parsable: $@"); + return 0; + } + + my $match_trusted_proxy = 0; + + if ($self->{trusted_proxy_ips}) { + foreach my $t (@{$self->{trusted_proxy_ips}}) { + if ($t->overlaps($cip)) { + $match_trusted_proxy = 1; + $self->dprint("client IP in trusted proxies: ". $t->print()); + last; + } + } + } else { + $match_trusted_proxy = 1; + } + + return $match_trusted_proxy; +} + sub accept_connections { my ($self) = @_; diff --git a/src/PVE/APIServer/Utils.pm b/src/PVE/APIServer/Utils.pm index 5728d97..f7cff29 100644 --- a/src/PVE/APIServer/Utils.pm +++ b/src/PVE/APIServer/Utils.pm @@ -26,6 +26,8 @@ sub read_proxy_config { $shcmd .= 'echo \"COMPRESSION:\$COMPRESSION\";'; $shcmd .= 'echo \"DISABLE_TLS_1_2:\$DISABLE_TLS_1_2\";'; $shcmd .= 'echo \"DISABLE_TLS_1_3:\$DISABLE_TLS_1_3\";'; + $shcmd .= 'echo \"PROXY_REAL_IP_HEADER:\$PROXY_REAL_IP_HEADER\";'; + $shcmd .= 'echo \"TRUSTED_PROXY_IPS:\$TRUSTED_PROXY_IPS\";'; my $data = -f $conffile ? `bash -c "$shcmd"` : ''; @@ -65,6 +67,19 @@ sub read_proxy_config { $res->{$key} = $value; } elsif ($key eq 'TLS_KEY_FILE') { $res->{$key} = $value; + } elsif ($key eq 'PROXY_REAL_IP_HEADER') { + $res->{$key} = $value; + } elsif ($key eq 'TRUSTED_PROXY_IPS') { + my $ips = []; + foreach my $ip (split(/,/, $value)) { + if ($ip eq 'all') { + push @$ips, Net::IP->new('0/0') || die Net::IP::Error() . "\n"; + push @$ips, Net::IP->new('::/0') || die Net::IP::Error() . "\n"; + next; + } + push @$ips, Net::IP->new(normalize_v4_in_v6($ip)) || die Net::IP::Error() . "\n"; + } + $res->{$key} = $ips; } elsif (grep { $key eq $_ } @$boolean_options) { die "unknown value '$value' - use 0 or 1\n" if $value !~ m/^(0|1)$/; $res->{$key} = $value; -- 2.39.2 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [pve-devel] [PATCH http-server 1/1] fix #5699: pveproxy: add library methods for real IP support 2024-09-10 0:30 ` [pve-devel] [PATCH http-server 1/1] fix #5699: pveproxy: add library methods " Thomas Skinner @ 2024-11-13 11:34 ` Fabian Grünbichler 2024-11-24 23:49 ` Thomas Skinner 0 siblings, 1 reply; 12+ messages in thread From: Fabian Grünbichler @ 2024-11-13 11:34 UTC (permalink / raw) To: Proxmox VE development discussion On September 10, 2024 2:30 am, Thomas Skinner wrote: > --- > src/PVE/APIServer/AnyEvent.pm | 43 ++++++++++++++++++++++++++++++++--- > src/PVE/APIServer/Utils.pm | 15 ++++++++++++ > 2 files changed, 55 insertions(+), 3 deletions(-) > > diff --git a/src/PVE/APIServer/AnyEvent.pm b/src/PVE/APIServer/AnyEvent.pm > index a8d60c1..c2afb4d 100644 > --- a/src/PVE/APIServer/AnyEvent.pm > +++ b/src/PVE/APIServer/AnyEvent.pm > @@ -85,20 +85,21 @@ sub log_request { > > my $loginfo = $reqstate->{log}; > > - # like apache2 common log format > - # LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" > + # like apache2 common log format + client IP address > + # LogFormat "%a %h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" this has the potential to break a lot of things analysing this log file.. would it make sense to only do it conditionally (if a "real IP" is set)? or *just* log the "real IP" instead of the peerip (which would be backwards-compatible as well)? > > return if $loginfo->{written}; # avoid duplicate logs > $loginfo->{written} = 1; > > my $peerip = $reqstate->{peer_host} || '-'; > + my $realip = $loginfo->{real_ip} || $peerip; > my $userid = $loginfo->{userid} || '-'; > my $content_length = defined($loginfo->{content_length}) ? $loginfo->{content_length} : '-'; > my $code = $loginfo->{code} || 500; > my $requestline = $loginfo->{requestline} || '-'; > my $timestr = strftime("%d/%m/%Y:%H:%M:%S %z", localtime()); > > - my $msg = "$peerip - $userid [$timestr] \"$requestline\" $code $content_length\n"; > + my $msg = "$realip $peerip - $userid [$timestr] \"$requestline\" $code $content_length\n"; > > $self->write_log($msg); > } > @@ -1462,6 +1463,14 @@ sub authenticate_and_handle_request { > > my $auth = {}; > > + if ($self->{proxy_real_ip_header} && $request->header($self->{proxy_real_ip_header})) { > + my $real_ip = Net::IP->new($request->header($self->{proxy_real_ip_header})) || undef; > + $reqstate->{log}->{real_ip} = Net::IP::ip_compress_address( > + $real_ip->ip(), > + $real_ip->version(), > + ) if defined($real_ip) && $self->check_trusted_proxy($reqstate->{peer_host}); the alignment/indentation is off there, and the mixing of ifs and postifs is also not easily readable.. maybe: if (my $hdr = $self->{proxy_real_ip_header}) { if (my $value = $request->header($hdr})) { my $real_ip = Net::IP->new($value); if (defined($real_ip) && $self->check_trusted_proxy($peerip)) { $reqstate->{log}->{real_ip} = Net::IP::ip_compress_address( $real_ip->ip(), $real_ip->version(), ); } } } > + } > + > if ($self->{spiceproxy}) { > my $connect_str = $request->header('Host'); > my ($vmid, $node, $port) = $self->verify_spice_connect_url($connect_str); > @@ -1801,6 +1810,34 @@ sub check_host_access { > return $match_allow; > } > > +sub check_trusted_proxy { > + my ($self, $clientip) = @_; > + > + $clientip = PVE::APIServer::Utils::normalize_v4_in_v6($clientip); > + my $cip = Net::IP->new($clientip); > + > + if (!$cip) { > + $self->dprint("client IP not parsable: $@"); > + return 0; > + } > + > + my $match_trusted_proxy = 0; this is not needed, you can just return directly below.. > + > + if ($self->{trusted_proxy_ips}) { > + foreach my $t (@{$self->{trusted_proxy_ips}}) { this line doesn't really match our perl style ;) > + if ($t->overlaps($cip)) { > + $match_trusted_proxy = 1; > + $self->dprint("client IP in trusted proxies: ". $t->print()); > + last; > + } > + } > + } else { > + $match_trusted_proxy = 1; > + } > + > + return $match_trusted_proxy; > +} > + > sub accept_connections { > my ($self) = @_; > > diff --git a/src/PVE/APIServer/Utils.pm b/src/PVE/APIServer/Utils.pm > index 5728d97..f7cff29 100644 > --- a/src/PVE/APIServer/Utils.pm > +++ b/src/PVE/APIServer/Utils.pm > @@ -26,6 +26,8 @@ sub read_proxy_config { > $shcmd .= 'echo \"COMPRESSION:\$COMPRESSION\";'; > $shcmd .= 'echo \"DISABLE_TLS_1_2:\$DISABLE_TLS_1_2\";'; > $shcmd .= 'echo \"DISABLE_TLS_1_3:\$DISABLE_TLS_1_3\";'; > + $shcmd .= 'echo \"PROXY_REAL_IP_HEADER:\$PROXY_REAL_IP_HEADER\";'; > + $shcmd .= 'echo \"TRUSTED_PROXY_IPS:\$TRUSTED_PROXY_IPS\";'; > > my $data = -f $conffile ? `bash -c "$shcmd"` : ''; > > @@ -65,6 +67,19 @@ sub read_proxy_config { > $res->{$key} = $value; > } elsif ($key eq 'TLS_KEY_FILE') { > $res->{$key} = $value; > + } elsif ($key eq 'PROXY_REAL_IP_HEADER') { > + $res->{$key} = $value; > + } elsif ($key eq 'TRUSTED_PROXY_IPS') { > + my $ips = []; > + foreach my $ip (split(/,/, $value)) { > + if ($ip eq 'all') { > + push @$ips, Net::IP->new('0/0') || die Net::IP::Error() . "\n"; > + push @$ips, Net::IP->new('::/0') || die Net::IP::Error() . "\n"; > + next; > + } > + push @$ips, Net::IP->new(normalize_v4_in_v6($ip)) || die Net::IP::Error() . "\n"; > + } > + $res->{$key} = $ips; > } elsif (grep { $key eq $_ } @$boolean_options) { > die "unknown value '$value' - use 0 or 1\n" if $value !~ m/^(0|1)$/; > $res->{$key} = $value; > -- > 2.39.2 > > > _______________________________________________ > pve-devel mailing list > pve-devel@lists.proxmox.com > https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel > > > _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [pve-devel] [PATCH http-server 1/1] fix #5699: pveproxy: add library methods for real IP support 2024-11-13 11:34 ` Fabian Grünbichler @ 2024-11-24 23:49 ` Thomas Skinner 2024-11-25 8:31 ` Thomas Lamprecht 0 siblings, 1 reply; 12+ messages in thread From: Thomas Skinner @ 2024-11-24 23:49 UTC (permalink / raw) To: f.gruenbichler; +Cc: pve-devel > On September 10, 2024 2:30 am, Thomas Skinner wrote: >> --- >> src/PVE/APIServer/AnyEvent.pm | 43 ++++++++++++++++++++++++++++++++--- >> src/PVE/APIServer/Utils.pm | 15 ++++++++++++ >> 2 files changed, 55 insertions(+), 3 deletions(-) >> >> diff --git a/src/PVE/APIServer/AnyEvent.pm b/src/PVE/APIServer/AnyEvent.pm >> index a8d60c1..c2afb4d 100644 >> --- a/src/PVE/APIServer/AnyEvent.pm >> +++ b/src/PVE/APIServer/AnyEvent.pm >> @@ -85,20 +85,21 @@ sub log_request { >> >> my $loginfo = $reqstate->{log}; >> >> - # like apache2 common log format >> - # LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" >> + # like apache2 common log format + client IP address >> + # LogFormat "%a %h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" > > this has the potential to break a lot of things analysing this log > file.. would it make sense to only do it conditionally (if a "real IP" is set)? > > or *just* log the "real IP" instead of the peerip (which would be > backwards-compatible as well)? I agree that it would break existing log analyzers. I also believe it's important to have the option to log the peer/proxy IP as well (in the event there are multiple proxies or that the proxy itself is the source). I'll change the formatting back to what was there previously and add an option to log the peer IP as well. >> >> return if $loginfo->{written}; # avoid duplicate logs >> $loginfo->{written} = 1; >> >> my $peerip = $reqstate->{peer_host} || '-'; >> + my $realip = $loginfo->{real_ip} || $peerip; >> my $userid = $loginfo->{userid} || '-'; >> my $content_length = defined($loginfo->{content_length}) ? $loginfo->{content_length} : '-'; >> my $code = $loginfo->{code} || 500; >> my $requestline = $loginfo->{requestline} || '-'; >> my $timestr = strftime("%d/%m/%Y:%H:%M:%S %z", localtime()); >> >> - my $msg = "$peerip - $userid [$timestr] \"$requestline\" $code $content_length\n"; >> + my $msg = "$realip $peerip - $userid [$timestr] \"$requestline\" $code $content_length\n"; >> >> $self->write_log($msg); >> } >> @@ -1462,6 +1463,14 @@ sub authenticate_and_handle_request { >> >> my $auth = {}; >> >> + if ($self->{proxy_real_ip_header} && $request->header($self->{proxy_real_ip_header})) { >> + my $real_ip = Net::IP->new($request->header($self->{proxy_real_ip_header})) || undef; >> + $reqstate->{log}->{real_ip} = Net::IP::ip_compress_address( >> + $real_ip->ip(), >> + $real_ip->version(), >> + ) if defined($real_ip) && $self->check_trusted_proxy($reqstate->{peer_host}); > > the alignment/indentation is off there, and the mixing of ifs and > postifs is also not easily readable.. maybe: > > if (my $hdr = $self->{proxy_real_ip_header}) { > if (my $value = $request->header($hdr})) { > my $real_ip = Net::IP->new($value); > if (defined($real_ip) && $self->check_trusted_proxy($peerip)) { > $reqstate->{log}->{real_ip} = Net::IP::ip_compress_address( > $real_ip->ip(), > $real_ip->version(), > ); > } > } > } I agree. After looking at my code compared to yours, mine is rather messy. Will fix with a combination of the two. >> + } >> + >> if ($self->{spiceproxy}) { >> my $connect_str = $request->header('Host'); >> my ($vmid, $node, $port) = $self->verify_spice_connect_url($connect_str); >> @@ -1801,6 +1810,34 @@ sub check_host_access { >> return $match_allow; >> } >> >> +sub check_trusted_proxy { >> + my ($self, $clientip) = @_; >> + >> + $clientip = PVE::APIServer::Utils::normalize_v4_in_v6($clientip); >> + my $cip = Net::IP->new($clientip); >> + >> + if (!$cip) { >> + $self->dprint("client IP not parsable: $@"); >> + return 0; >> + } >> + >> + my $match_trusted_proxy = 0; > > this is not needed, you can just return directly below.. Yep, that's accurate. I had it there to match the style of the check_host_access subroutine for consistency. Will adjust accordingly. >> + >> + if ($self->{trusted_proxy_ips}) { >> + foreach my $t (@{$self->{trusted_proxy_ips}}) { > > this line doesn't really match our perl style ;) I'm not sure what you're referring to here. I copied and modified existing code that did nearly what I needed from the check_host_access subroutine. Could you elaborate or give an example of what needs to be fixed here? >> + if ($t->overlaps($cip)) { >> + $match_trusted_proxy = 1; >> + $self->dprint("client IP in trusted proxies: ". $t->print()); >> + last; >> + } >> + } >> + } else { >> + $match_trusted_proxy = 1; >> + } >> + >> + return $match_trusted_proxy; >> +} >> + >> sub accept_connections { >> my ($self) = @_; >> >> diff --git a/src/PVE/APIServer/Utils.pm b/src/PVE/APIServer/Utils.pm >> index 5728d97..f7cff29 100644 >> --- a/src/PVE/APIServer/Utils.pm >> +++ b/src/PVE/APIServer/Utils.pm >> @@ -26,6 +26,8 @@ sub read_proxy_config { >> $shcmd .= 'echo \"COMPRESSION:\$COMPRESSION\";'; >> $shcmd .= 'echo \"DISABLE_TLS_1_2:\$DISABLE_TLS_1_2\";'; >> $shcmd .= 'echo \"DISABLE_TLS_1_3:\$DISABLE_TLS_1_3\";'; >> + $shcmd .= 'echo \"PROXY_REAL_IP_HEADER:\$PROXY_REAL_IP_HEADER\";'; >> + $shcmd .= 'echo \"TRUSTED_PROXY_IPS:\$TRUSTED_PROXY_IPS\";'; >> >> my $data = -f $conffile ? `bash -c "$shcmd"` : ''; >> >> @@ -65,6 +67,19 @@ sub read_proxy_config { >> $res->{$key} = $value; >> } elsif ($key eq 'TLS_KEY_FILE') { >> $res->{$key} = $value; >> + } elsif ($key eq 'PROXY_REAL_IP_HEADER') { >> + $res->{$key} = $value; >> + } elsif ($key eq 'TRUSTED_PROXY_IPS') { >> + my $ips = []; >> + foreach my $ip (split(/,/, $value)) { >> + if ($ip eq 'all') { >> + push @$ips, Net::IP->new('0/0') || die Net::IP::Error() . "\n"; >> + push @$ips, Net::IP->new('::/0') || die Net::IP::Error() . "\n"; >> + next; >> + } >> + push @$ips, Net::IP->new(normalize_v4_in_v6($ip)) || die Net::IP::Error() . "\n"; >> + } >> + $res->{$key} = $ips; >> } elsif (grep { $key eq $_ } @$boolean_options) { >> die "unknown value '$value' - use 0 or 1\n" if $value !~ m/^(0|1)$/; >> $res->{$key} = $value; >> -- >> 2.39.2 >> >> >> _______________________________________________ >> pve-devel mailing list >> pve-devel@lists.proxmox.com >> https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel >> >> >> > > > _______________________________________________ > pve-devel mailing list > pve-devel@lists.proxmox.com > https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [pve-devel] [PATCH http-server 1/1] fix #5699: pveproxy: add library methods for real IP support 2024-11-24 23:49 ` Thomas Skinner @ 2024-11-25 8:31 ` Thomas Lamprecht 2024-11-25 9:05 ` Fabian Grünbichler 0 siblings, 1 reply; 12+ messages in thread From: Thomas Lamprecht @ 2024-11-25 8:31 UTC (permalink / raw) To: Proxmox VE development discussion, Thomas Skinner, f.gruenbichler Am 25.11.24 um 00:49 schrieb Thomas Skinner: >> On September 10, 2024 2:30 am, Thomas Skinner wrote: >>> --- >>> src/PVE/APIServer/AnyEvent.pm | 43 ++++++++++++++++++++++++++++++++--- >>> src/PVE/APIServer/Utils.pm | 15 ++++++++++++ >>> 2 files changed, 55 insertions(+), 3 deletions(-) >>> >>> diff --git a/src/PVE/APIServer/AnyEvent.pm b/src/PVE/APIServer/AnyEvent.pm >>> index a8d60c1..c2afb4d 100644 >>> --- a/src/PVE/APIServer/AnyEvent.pm >>> +++ b/src/PVE/APIServer/AnyEvent.pm >>> @@ -85,20 +85,21 @@ sub log_request { >>> >>> my $loginfo = $reqstate->{log}; >>> >>> - # like apache2 common log format >>> - # LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" >>> + # like apache2 common log format + client IP address >>> + # LogFormat "%a %h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" >> >> this has the potential to break a lot of things analysing this log >> file.. would it make sense to only do it conditionally (if a "real IP" is set)? >> >> or *just* log the "real IP" instead of the peerip (which would be >> backwards-compatible as well)? > > I agree that it would break existing log analyzers. I also believe it's important > to have the option to log the peer/proxy IP as well (in the event there are multiple > proxies or that the proxy itself is the source). I'll change the formatting back to > what was there previously and add an option to log the peer IP as well. > Note that people have fail2ban setup on these logs, among other things, and that breaking suddenly is _far_ from ideal. If always done it should be on a major release, that avoids the headache of keeping (work for devs) and detecting the existence of that option (work for the user). But not a must, we can also guard this with some option or the like now. >>> + >>> + if ($self->{trusted_proxy_ips}) { >>> + foreach my $t (@{$self->{trusted_proxy_ips}}) { >> >> this line doesn't really match our perl style ;) > > I'm not sure what you're referring to here. I copied and modified existing code > that did nearly what I needed from the check_host_access subroutine. Could you > elaborate or give an example of what needs to be fixed here? > Yeah, our existing code is not all that holy, we're moving that over slowly, e.g. when touching some code parts anyway. The goal would be for all code to follow this document giving some basic guidelines: https://pve.proxmox.com/wiki/Perl_Style_Guide One of the important one is to avoid single character variables and needles abbreviation (cip vs client_ip), as that has _no_ gain and just makes everyone's life harder (and yeah, our existing code base has a few of those). Then there are some more minor style issues. Here you could write: if (my $trusted_proxy_ips = $self->{trusted_proxy_ips}) { for my $trusted_net ($trusted_proxy_ips->@*) { if ($trusted_net->overlaps($client_ip)) { $self->dprint("client IP '$client_ip' in allowed proxy network '". $t->print()."'"); return 1; } } } return 0; and maybe change the name, e.g. like: s/trusted_proxy_ips/proxy_cidr_allowlist/ _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [pve-devel] [PATCH http-server 1/1] fix #5699: pveproxy: add library methods for real IP support 2024-11-25 8:31 ` Thomas Lamprecht @ 2024-11-25 9:05 ` Fabian Grünbichler 2024-11-25 11:17 ` Thomas Lamprecht 0 siblings, 1 reply; 12+ messages in thread From: Fabian Grünbichler @ 2024-11-25 9:05 UTC (permalink / raw) To: Thomas Lamprecht, Proxmox VE development discussion, Thomas Skinner > Thomas Lamprecht <t.lamprecht@proxmox.com> hat am 25.11.2024 09:31 CET geschrieben: > > > Am 25.11.24 um 00:49 schrieb Thomas Skinner: > >> On September 10, 2024 2:30 am, Thomas Skinner wrote: > >>> --- > >>> src/PVE/APIServer/AnyEvent.pm | 43 ++++++++++++++++++++++++++++++++--- > >>> src/PVE/APIServer/Utils.pm | 15 ++++++++++++ > >>> 2 files changed, 55 insertions(+), 3 deletions(-) > >>> > >>> diff --git a/src/PVE/APIServer/AnyEvent.pm b/src/PVE/APIServer/AnyEvent.pm > >>> index a8d60c1..c2afb4d 100644 > >>> --- a/src/PVE/APIServer/AnyEvent.pm > >>> +++ b/src/PVE/APIServer/AnyEvent.pm > >>> @@ -85,20 +85,21 @@ sub log_request { > >>> > >>> my $loginfo = $reqstate->{log}; > >>> > >>> - # like apache2 common log format > >>> - # LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" > >>> + # like apache2 common log format + client IP address > >>> + # LogFormat "%a %h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" > >> > >> this has the potential to break a lot of things analysing this log > >> file.. would it make sense to only do it conditionally (if a "real IP" is set)? > >> > >> or *just* log the "real IP" instead of the peerip (which would be > >> backwards-compatible as well)? > > > > I agree that it would break existing log analyzers. I also believe it's important > > to have the option to log the peer/proxy IP as well (in the event there are multiple > > proxies or that the proxy itself is the source). I'll change the formatting back to > > what was there previously and add an option to log the peer IP as well. > > > > Note that people have fail2ban setup on these logs, among other things, > and that breaking suddenly is _far_ from ideal. If always done it should > be on a major release, that avoids the headache of keeping (work for devs) > and detecting the existence of that option (work for the user). > > But not a must, we can also guard this with some option or the like now. yeah, we could switch to the new format *only* if the header option is set? as else, the two IPs are identical anyway, so logging the same one twice while breaking the format provides no benefit? and then maybe with 9.0 switch the format unconditionally, so that parsers/fail2ban configs only need to handle one of them going forward.. _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [pve-devel] [PATCH http-server 1/1] fix #5699: pveproxy: add library methods for real IP support 2024-11-25 9:05 ` Fabian Grünbichler @ 2024-11-25 11:17 ` Thomas Lamprecht 2024-11-25 11:31 ` Fabian Grünbichler 0 siblings, 1 reply; 12+ messages in thread From: Thomas Lamprecht @ 2024-11-25 11:17 UTC (permalink / raw) To: Proxmox VE development discussion, Fabian Grünbichler, Thomas Skinner Am 25.11.24 um 10:05 schrieb Fabian Grünbichler: > yeah, we could switch to the new format *only* if the header option is set? > as else, the two IPs are identical anyway, so logging the same one twice > while breaking the format provides no benefit? > > and then maybe with 9.0 switch the format unconditionally, so that > parsers/fail2ban configs only need to handle one of them going forward.. Sounds fine to me. Albeit for some this still might break, if they already use a reverse proxy now – but these people at least could not have any (working) fail2ban, as they just would have banned the IP of their reverse proxy, so it should be fine I think. btw., and sorry if I just missed this on reading, how do others log this? I.e., is there any somewhat common format and does this patch already adheres to that format? _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [pve-devel] [PATCH http-server 1/1] fix #5699: pveproxy: add library methods for real IP support 2024-11-25 11:17 ` Thomas Lamprecht @ 2024-11-25 11:31 ` Fabian Grünbichler 2024-11-25 23:41 ` Thomas Skinner 0 siblings, 1 reply; 12+ messages in thread From: Fabian Grünbichler @ 2024-11-25 11:31 UTC (permalink / raw) To: Thomas Lamprecht, Proxmox VE development discussion, Thomas Skinner > Thomas Lamprecht <t.lamprecht@proxmox.com> hat am 25.11.2024 12:17 CET geschrieben: > > > Am 25.11.24 um 10:05 schrieb Fabian Grünbichler: > > yeah, we could switch to the new format *only* if the header option is set? > > as else, the two IPs are identical anyway, so logging the same one twice > > while breaking the format provides no benefit? > > > > and then maybe with 9.0 switch the format unconditionally, so that > > parsers/fail2ban configs only need to handle one of them going forward.. > > Sounds fine to me. Albeit for some this still might break, if they already > use a reverse proxy now – but these people at least could not have any > (working) fail2ban, as they just would have banned the IP of their reverse > proxy, so it should be fine I think. it would still require enabling the new feature on the pveproxy side (that's what I meant with "header option", not that the default header is set on the HTTP request), so it's completely opt-in? > btw., and sorry if I just missed this on reading, how do others log this? > I.e., is there any somewhat common format and does this patch already > adheres to that format? that would indeed be nice to know! :) _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [pve-devel] [PATCH http-server 1/1] fix #5699: pveproxy: add library methods for real IP support 2024-11-25 11:31 ` Fabian Grünbichler @ 2024-11-25 23:41 ` Thomas Skinner 0 siblings, 0 replies; 12+ messages in thread From: Thomas Skinner @ 2024-11-25 23:41 UTC (permalink / raw) To: Fabian Grünbichler Cc: Proxmox VE development discussion, Thomas Lamprecht On Mon, Nov 25, 2024 at 5:31 AM Fabian Grünbichler <f.gruenbichler@proxmox.com> wrote: > > > > Thomas Lamprecht <t.lamprecht@proxmox.com> hat am 25.11.2024 12:17 CET geschrieben: > > > > > > Am 25.11.24 um 10:05 schrieb Fabian Grünbichler: > > > yeah, we could switch to the new format *only* if the header option is set? > > > as else, the two IPs are identical anyway, so logging the same one twice > > > while breaking the format provides no benefit? > > > > > > and then maybe with 9.0 switch the format unconditionally, so that > > > parsers/fail2ban configs only need to handle one of them going forward.. > > > > Sounds fine to me. Albeit for some this still might break, if they already > > use a reverse proxy now – but these people at least could not have any > > (working) fail2ban, as they just would have banned the IP of their reverse > > proxy, so it should be fine I think. > > it would still require enabling the new feature on the pveproxy side (that's what I meant with "header option", not that the default header is set on the HTTP request), so it's completely opt-in? Yes, to log the real client IP from the proxy, the header would need to be set in the PROXY_REAL_IP_HEADER option anyway to even get the data. If anything, having the option would help those with the "broken" fail2ban have the real client IP data instead of the proxy IP. I don't have any issues with keeping the format as-is and logging the real IP instead of the peer IP. This can be covered in the docs and would have to be explicitly enabled to change the current logging behavior. This way the patch doesn't break any existing log analyzer but the feature is there available for the user. The TRUSTED_PROXY_IPS helps with the risk of not logging the proxy/peer IP by limiting the list of sources that can set the header. > > btw., and sorry if I just missed this on reading, how do others log this? > > I.e., is there any somewhat common format and does this patch already > > adheres to that format? > > that would indeed be nice to know! :) > I'm not sure that there is or is not a standard for logging the client IP and peer/proxy IP information together. In my experience, it has been a custom log format (specifically in Apache httpd) to output both of them. Across products, for web-based logs, I've seen Apache/NCSA style, semi-structured key/value text, or structured JSON objects. I'd argue that the JSON is the most compatible since it explicitly labels the data fields, but that would be a breaking change from what PVE is doing now for logging, and it's not friendly for human readability. _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel ^ permalink raw reply [flat|nested] 12+ messages in thread
* [pve-devel] [PATCH manager 1/1] fix #5699: pveproxy: add settings for real IP support 2024-09-10 0:30 [pve-devel] [PATCH SERIES manager/http-server/docs] fix #5699: add support for real IP Thomas Skinner 2024-09-10 0:30 ` [pve-devel] [PATCH docs 1/1] fix #5699: pveproxy: add docs for real IP support Thomas Skinner 2024-09-10 0:30 ` [pve-devel] [PATCH http-server 1/1] fix #5699: pveproxy: add library methods " Thomas Skinner @ 2024-09-10 0:30 ` Thomas Skinner 2 siblings, 0 replies; 12+ messages in thread From: Thomas Skinner @ 2024-09-10 0:30 UTC (permalink / raw) To: pve-devel --- PVE/Service/pveproxy.pm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/PVE/Service/pveproxy.pm b/PVE/Service/pveproxy.pm index ac108545..66db7a73 100755 --- a/PVE/Service/pveproxy.pm +++ b/PVE/Service/pveproxy.pm @@ -115,6 +115,8 @@ sub init { honor_cipher_order => $proxyconf->{HONOR_CIPHER_ORDER}, }, compression => $proxyconf->{COMPRESSION}, + proxy_real_ip_header => $proxyconf->{PROXY_REAL_IP_HEADER}, + trusted_proxy_ips => $proxyconf->{TRUSTED_PROXY_IPS}, # Note: there is no authentication for those pages and dirs! pages => { '/' => sub { get_index($self->{nodename}, @_) }, -- 2.39.2 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel ^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2024-11-25 23:42 UTC | newest] Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2024-09-10 0:30 [pve-devel] [PATCH SERIES manager/http-server/docs] fix #5699: add support for real IP Thomas Skinner 2024-09-10 0:30 ` [pve-devel] [PATCH docs 1/1] fix #5699: pveproxy: add docs for real IP support Thomas Skinner 2024-11-13 11:36 ` Fabian Grünbichler 2024-09-10 0:30 ` [pve-devel] [PATCH http-server 1/1] fix #5699: pveproxy: add library methods " Thomas Skinner 2024-11-13 11:34 ` Fabian Grünbichler 2024-11-24 23:49 ` Thomas Skinner 2024-11-25 8:31 ` Thomas Lamprecht 2024-11-25 9:05 ` Fabian Grünbichler 2024-11-25 11:17 ` Thomas Lamprecht 2024-11-25 11:31 ` Fabian Grünbichler 2024-11-25 23:41 ` Thomas Skinner 2024-09-10 0:30 ` [pve-devel] [PATCH manager 1/1] fix #5699: pveproxy: add settings " Thomas Skinner
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox