Setting[:remote_addr] in ip_from_request_env in unattended_controller.rb

I've been working through troubleshooting template requests via a
smart-proxy without a token present.

The request is from the client IP, which gets masked by the proxy on
forward, but when I reviewed the code I noticed that the proxy inserts an
X-Forwarded-For header, which foreman's unattended_controller looks at and
is supposed to honour.

However, I can't make sense of the following code:

def ip_from_request_env
ip = request.env['REMOTE_ADDR']

# check if someone is asking on behalf of another system (load balance 

etc)
if request.env['HTTP_X_FORWARDED_FOR'].present? and (ip =~
Regexp.new(Setting[:remote_addr]))
ip = request.env['HTTP_X_FORWARDED_FOR']
end

ip

end

In my environment, foreman is running behind passenger, and the remote_addr
setting is 127.0.0.1.

When a request comes from a smart-proxy, the Regexp.new check fails, and
'ip' never gets set to the X-Forwarded-For IP, it retains the proxy's
source IP, which fails to render.

If I change the code to:

if request.env['HTTP_X_FORWARDED_FOR'].present?

everything works as I expect it to.

Am I missing something here, or is the code not quite right?

If I recall correctly (disclaimer, it's been a while :P), the default of
127.0.0.1 is a security default which will prevent matching anything on the
network spoofing hosts without a valid token. This is the only sane default
when we (a priori) know little about the users network setup. I think you
just need to set :remote_addr to a regular expression which will match the
Smart Proxy's IP, so that the second half will succeed and use the header.

Greg

··· On 17 July 2016 at 06:42, adiran wrote:

I’ve been working through troubleshooting template requests via a
smart-proxy without a token present.

The request is from the client IP, which gets masked by the proxy on
forward, but when I reviewed the code I noticed that the proxy inserts an
X-Forwarded-For header, which foreman’s unattended_controller looks at and
is supposed to honour.

However, I can’t make sense of the following code:

def ip_from_request_env
ip = request.env[‘REMOTE_ADDR’]

# check if someone is asking on behalf of another system (load balance

etc)
if request.env[‘HTTP_X_FORWARDED_FOR’].present? and (ip =~
Regexp.new(Setting[:remote_addr]))
ip = request.env[‘HTTP_X_FORWARDED_FOR’]
end

ip

end

In my environment, foreman is running behind passenger, and the
remote_addr setting is 127.0.0.1.

When a request comes from a smart-proxy, the Regexp.new check fails, and
’ip’ never gets set to the X-Forwarded-For IP, it retains the proxy’s
source IP, which fails to render.

If I change the code to:

if request.env['HTTP_X_FORWARDED_FOR'].present?

everything works as I expect it to.

Am I missing something here, or is the code not quite right?

Ok, this makes a little more sense. So in an environment with many
smart-proxies, the IP of each one would need to be included in this regexp?

I just assumed that foreman would take care of accepting X-Forwarded-For
from smart-proxy IPs which are registered, since it already knows about
them.

This setting could use a little more detail in the manual, glad you recall.

··· On Sunday, July 17, 2016 at 9:34:10 AM UTC-4, Greg Sutcliffe wrote: > > On 17 July 2016 at 06:42, adiran <adrian...@gmail.com > > wrote: > >> I've been working through troubleshooting template requests via a >> smart-proxy without a token present. >> >> The request is from the client IP, which gets masked by the proxy on >> forward, but when I reviewed the code I noticed that the proxy inserts an >> X-Forwarded-For header, which foreman's unattended_controller looks at and >> is supposed to honour. >> >> However, I can't make sense of the following code: >> >> def ip_from_request_env >> ip = request.env['REMOTE_ADDR'] >> >> # check if someone is asking on behalf of another system (load >> balance etc) >> if request.env['HTTP_X_FORWARDED_FOR'].present? and (ip =~ >> Regexp.new(Setting[:remote_addr])) >> ip = request.env['HTTP_X_FORWARDED_FOR'] >> end >> >> ip >> end >> >> In my environment, foreman is running behind passenger, and the >> remote_addr setting is 127.0.0.1. >> >> When a request comes from a smart-proxy, the Regexp.new check fails, and >> 'ip' never gets set to the X-Forwarded-For IP, it retains the proxy's >> source IP, which fails to render. >> >> If I change the code to: >> >> if request.env['HTTP_X_FORWARDED_FOR'].present? >> >> everything works as I expect it to. >> >> Am I missing something here, or is the code not quite right? >> > > If I recall correctly (disclaimer, it's been a while :P), the default of > 127.0.0.1 is a security default which will prevent matching anything on the > network spoofing hosts without a valid token. This is the only sane default > when we (a priori) know little about the users network setup. I think you > just need to set :remote_addr to a regular expression which will match the > Smart Proxy's IP, so that the second half will succeed and use the header. > > Greg >

> Ok, this makes a little more sense. So in an environment with many
> smart-proxies, the IP of each one would need to be included in this regexp?
>
> I just assumed that foreman would take care of accepting X-Forwarded-For
> from smart-proxy IPs which are registered, since it already knows about
> them.
>

I'm away from my desk, so I'm working from memory, but I think this
predates the smart-proxy-auth system that came in later for validating
things like ENC requests from valid proxies. The main issue there is that
the proxy-auth relies on validating the names in the SSL certificate -
templates usually aren't acquired over https, so this wouldn't work here.
It might be possible to adapt / re-use some of those ideas though - I do
agree that it's non-intuitive compared to how ENC/reports works.

> This setting could use a little more detail in the manual, glad you recall.
>

Sure, feel free -


:slight_smile:

Greg

··· On 17 July 2016 at 16:22, adiran wrote:

I cloned the repo, modified the doc section, and committed.

First time doing this on github, so if I've done something backwards, or
broken any code etiquette, I stand to be corrected. :wink:

··· On Sunday, July 17, 2016 at 11:38:23 AM UTC-4, Greg Sutcliffe wrote: > > On 17 July 2016 at 16:22, adiran <adrian...@gmail.com > > wrote: > >> Ok, this makes a little more sense. So in an environment with many >> smart-proxies, the IP of each one would need to be included in this regexp? >> >> I just assumed that foreman would take care of accepting X-Forwarded-For >> from smart-proxy IPs which are registered, since it already knows about >> them. >> > > I'm away from my desk, so I'm working from memory, but I think this > predates the smart-proxy-auth system that came in later for validating > things like ENC requests from valid proxies. The main issue there is that > the proxy-auth relies on validating the names in the SSL certificate - > templates usually aren't acquired over https, so this wouldn't work here. > It might be possible to adapt / re-use some of those ideas though - I do > agree that it's non-intuitive compared to how ENC/reports works. > > >> This setting could use a little more detail in the manual, glad you >> recall. >> > > Sure, feel free - > https://github.com/theforeman/theforeman.org/blob/gh-pages/_includes/manuals/1.12/3.5.2_configuration_options.md > :) > > Greg >

Nevermind, I can't push, so here's the diff:

diff --git a/_includes/manuals/1.12/3.5.2_configuration_options.md
b/_includes/manuals/1.12/3.5.2_conf
index 6eafb9b…b1b26dd 100644
— a/_includes/manuals/1.12/3.5.2_configuration_options.md
+++ b/_includes/manuals/1.12/3.5.2_configuration_options.md
@@ -385,7 +385,13 @@ See also: dns_conflict_timeout

remote_addr

-If Foreman is running behind Passenger or a remote load balancer, the IP
of this load balance should
+When foreman receives client web requests via a smart proxy, proxy or load
balancer, the original client source IP address is lost, replaced by the
smart proxy, proxy, or load balancers IP instead. For template requests
without a token, this causes a failure, because foreman can't match the
request against a valid host.

··· + +Smart proxies, and other devices if configured, can preserve the original client IP within an HTTP X-Forwarded-For header, which foreman can evaluate and use to match the request against a valid host. + +In order to prevent spoofing and provide some level of security, foreman will only evaluate X-Forwarded-For headers from devices which match the list of IPs configured here. + +This is a regular expression, so it can support several load balancers, i.e: (10.0.0.1|127.0.0.1) Default: 127.0.0.1
require_ssl_smart_proxies

On Sunday, July 17, 2016 at 1:29:16 PM UTC-4, adrian wrote:

I cloned the repo, modified the doc section, and committed.

First time doing this on github, so if I’ve done something backwards, or
broken any code etiquette, I stand to be corrected. :wink:

On Sunday, July 17, 2016 at 11:38:23 AM UTC-4, Greg Sutcliffe wrote:

On 17 July 2016 at 16:22, adiran adrian...@gmail.com wrote:

Ok, this makes a little more sense. So in an environment with many
smart-proxies, the IP of each one would need to be included in this regexp?

I just assumed that foreman would take care of accepting X-Forwarded-For
from smart-proxy IPs which are registered, since it already knows about
them.

I’m away from my desk, so I’m working from memory, but I think this
predates the smart-proxy-auth system that came in later for validating
things like ENC requests from valid proxies. The main issue there is that
the proxy-auth relies on validating the names in the SSL certificate -
templates usually aren’t acquired over https, so this wouldn’t work here.
It might be possible to adapt / re-use some of those ideas though - I do
agree that it’s non-intuitive compared to how ENC/reports works.

This setting could use a little more detail in the manual, glad you
recall.

Sure, feel free -
https://github.com/theforeman/theforeman.org/blob/gh-pages/_includes/manuals/1.12/3.5.2_configuration_options.md
:slight_smile:

Greg

If you push your branch to your GitHub fork, you can open up a pull
request from the web UI against the original repo. See
https://help.github.com/articles/using-pull-requests/ for details.

··· On 17/07/16 18:33, adrian wrote: > Nevermind, I can't push, so here's the diff:


Dominic Cleal
dominic@cleal.org

Thanks so much! I followed the article and created a pull request.

··· On Monday, July 18, 2016 at 8:59:25 AM UTC-4, Dominic Cleal wrote: > > On 17/07/16 18:33, adrian wrote: > > Nevermind, I can't push, so here's the diff: > > If you push your branch to your GitHub fork, you can open up a pull > request from the web UI against the original repo. See > https://help.github.com/articles/using-pull-requests/ for details. > > -- > Dominic Cleal > dom...@cleal.org >