Get "Invalid IP Address" as response for DHCP Post REST Call

Problem:
Want to set DHCP reservation using DHCP Smart Proxy REST Call
Example rest call:
curl -X POST https://foreman-smartproxy.example.com:8443/dhcp/192.168.1.0 --header ‘Content-Type: application/json’ -d ‘{“ip”:192.168.1.123,“mac”:00:11:22:11:22:33,“hostname”:test-dhcp-restapi.example.com}’
Expected outcome:
HTTP 200 (I guess) and the IP to be reserved on dhcp server e.g. in file /var/lib/dhcpd/dhcpd.leases:
host test-dhcp-restapi.example.com {
dynamic;
hardware ethernet 00:11:22:11:22:33;
fixed-address 192.168.1.123;

}
According to logs (below) it fails during ip address validation:
/usr/share/foreman-proxy/lib/proxy/validations.rb:17:in `validate_ip’
I checked the relevant code that validates the ip address

 16   def validate_ip ip
 17     raise Error, "Invalid IP Address #{ip}" unless ip =~ /(\d{1,3}\.){3}\d{1,3}/
 18     ip
 19   end

and I made sure, that the IP I used validates ok like this:
ruby -e ‘puts “invalid” unless “192.168.1.123” =~ /(\d{1,3}.){3}\d{1,3}/’

Since the IP isnt logged (see log below) it means, that IP is somehow lost during the request.

Foreman and Proxy versions:
both: 1.19.1
Foreman and Proxy plugin versions:
just enabled dhcp module by:

:enabled: https
:use_provider: dhcp_isc

Other relevant data:
/var/log/foreman-proxy/proxy.log:

E, [2019-06-25T11:23:25.769498 ] ERROR -- : Invalid IP Address 
D, [2019-06-25T11:23:25.769600 ] DEBUG -- : Invalid IP Address  (Proxy::Validations::Error)
/usr/share/foreman-proxy/lib/proxy/validations.rb:17:in `validate_ip'
/usr/share/foreman-proxy/modules/dhcp_common/server.rb:132:in `add_record'
/usr/share/foreman-proxy/modules/dhcp_common/isc/omapi_provider.rb:29:in `add_record'
/usr/share/foreman-proxy/modules/dhcp/dhcp_api.rb:98:in `block in <class:DhcpApi>'
/usr/share/gems/gems/sinatra-1.4.8/lib/sinatra/base.rb:1611:in `call'
/usr/share/gems/gems/sinatra-1.4.8/lib/sinatra/base.rb:1611:in `block in compile!'
/usr/share/gems/gems/sinatra-1.4.8/lib/sinatra/base.rb:975:in `[]'
/usr/share/gems/gems/sinatra-1.4.8/lib/sinatra/base.rb:975:in `block (3 levels) in route!'
/usr/share/gems/gems/sinatra-1.4.8/lib/sinatra/base.rb:994:in `route_eval'
/usr/share/gems/gems/sinatra-1.4.8/lib/sinatra/base.rb:975:in `block (2 levels) in route!'
/usr/share/gems/gems/sinatra-1.4.8/lib/sinatra/base.rb:1015:in `block in process_route'
/usr/share/gems/gems/sinatra-1.4.8/lib/sinatra/base.rb:1013:in `catch'
/usr/share/gems/gems/sinatra-1.4.8/lib/sinatra/base.rb:1013:in `process_route'
/usr/share/gems/gems/sinatra-1.4.8/lib/sinatra/base.rb:973:in `block in route!'
/usr/share/gems/gems/sinatra-1.4.8/lib/sinatra/base.rb:972:in `each'
/usr/share/gems/gems/sinatra-1.4.8/lib/sinatra/base.rb:972:in `route!'
/usr/share/gems/gems/sinatra-1.4.8/lib/sinatra/base.rb:1085:in `block in dispatch!'
/usr/share/gems/gems/sinatra-1.4.8/lib/sinatra/base.rb:1067:in `block in invoke'
/usr/share/gems/gems/sinatra-1.4.8/lib/sinatra/base.rb:1067:in `catch'
/usr/share/gems/gems/sinatra-1.4.8/lib/sinatra/base.rb:1067:in `invoke'
/usr/share/gems/gems/sinatra-1.4.8/lib/sinatra/base.rb:1082:in `dispatch!'
/usr/share/gems/gems/sinatra-1.4.8/lib/sinatra/base.rb:907:in `block in call!'
/usr/share/gems/gems/sinatra-1.4.8/lib/sinatra/base.rb:1067:in `block in invoke'
/usr/share/gems/gems/sinatra-1.4.8/lib/sinatra/base.rb:1067:in `catch'
/usr/share/gems/gems/sinatra-1.4.8/lib/sinatra/base.rb:1067:in `invoke'
/usr/share/gems/gems/sinatra-1.4.8/lib/sinatra/base.rb:907:in `call!'
/usr/share/gems/gems/sinatra-1.4.8/lib/sinatra/base.rb:895:in `call'
/usr/share/gems/gems/rack-1.6.4/lib/rack/methodoverride.rb:22:in `call'
/usr/share/gems/gems/rack-1.6.4/lib/rack/commonlogger.rb:33:in `call'
/usr/share/gems/gems/sinatra-1.4.8/lib/sinatra/base.rb:219:in `call'
/usr/share/foreman-proxy/lib/proxy/log.rb:109:in `call'
/usr/share/foreman-proxy/lib/proxy/request_id_middleware.rb:9:in `call'
/usr/share/gems/gems/rack-protection-1.5.3/lib/rack/protection/xss_header.rb:18:in `call'
/usr/share/gems/gems/rack-protection-1.5.3/lib/rack/protection/path_traversal.rb:16:in `call'
/usr/share/gems/gems/rack-protection-1.5.3/lib/rack/protection/json_csrf.rb:18:in `call'
/usr/share/gems/gems/rack-protection-1.5.3/lib/rack/protection/base.rb:49:in `call'
/usr/share/gems/gems/rack-protection-1.5.3/lib/rack/protection/base.rb:49:in `call'
/usr/share/gems/gems/rack-protection-1.5.3/lib/rack/protection/frame_options.rb:31:in `call'
/usr/share/gems/gems/rack-1.6.4/lib/rack/nulllogger.rb:9:in `call'
/usr/share/gems/gems/rack-1.6.4/lib/rack/head.rb:13:in `call'
/usr/share/gems/gems/sinatra-1.4.8/lib/sinatra/show_exceptions.rb:25:in `call'
/usr/share/gems/gems/sinatra-1.4.8/lib/sinatra/base.rb:182:in `call'
/usr/share/gems/gems/sinatra-1.4.8/lib/sinatra/base.rb:2013:in `call'
/usr/share/gems/gems/sinatra-1.4.8/lib/sinatra/base.rb:1487:in `block in call'
/usr/share/gems/gems/sinatra-1.4.8/lib/sinatra/base.rb:1787:in `synchronize'
/usr/share/gems/gems/sinatra-1.4.8/lib/sinatra/base.rb:1487:in `call'
/usr/share/gems/gems/rack-1.6.4/lib/rack/urlmap.rb:66:in `block in call'
/usr/share/gems/gems/rack-1.6.4/lib/rack/urlmap.rb:50:in `each'
/usr/share/gems/gems/rack-1.6.4/lib/rack/urlmap.rb:50:in `call'
/usr/share/gems/gems/rack-1.6.4/lib/rack/builder.rb:153:in `call'
/usr/share/gems/gems/rack-1.6.4/lib/rack/handler/webrick.rb:88:in `service'
/usr/share/ruby/webrick/httpserver.rb:138:in `service'
/usr/share/ruby/webrick/httpserver.rb:94:in `run'
/usr/share/ruby/webrick/server.rb:295:in `block in start_thread'

Hi,

I don’t have a 1.19 instance at hand to test this, so this is a wild guess, but I assume you have encountered some sort of type confusion here.
Could you try quoting the values in your JSON string, too? Like “ip”:“192.168.1.123”, etc.
From a little playing with irb shell, I think the JSON library does not like unquted IP addresses things like that. This does not answer the question why this results in “invalid IP Address” instead of a JSON parser error, but it is woth a try.

Regards

Unfortunately it is the same. I really tried hard before coming here. Of course I started with quotes (as documented in the api doc) and then tried without. The example I gave is without. It doesnt matter !

Alright, thanks for the feedback.
Just to be absolutely sure we are not running into any parser/formatting related problems, have you tried using urlencoded style data? That has worked for me in the past.
So, instead of

try the following syntax:

curl -X POST https://foreman-smartproxy.example.com:8443/dhcp/192.168.1.0 --data "hostname=test-dhcp-restapi.example.com&ip=192.168.1.123&mac=00:11:22:11:22:33"

Regards

Thank you for your quick replies ! I tested the new REST Call, but also the alternative syntax produce the exact same error - unfortunately.

Hi,

just wanted to let you know that I did not forget you. I had a lot to do the last cuple days and did not find time to look at the code.
If you did not find the issue yet, I would try to find time next week to look more into this topic. In case you resolved it by now, please let me know.

Regards

Thanks a lot for your reply. And no, the issue is still present and I’ve no idea how to resolve that.

In your JSON input you’re not quoting the values so that likely invalidates the entire JSON resulting in nil. Try this:

curl -X POST https://foreman-smartproxy.example.com:8443/dhcp/192.168.1.0 --header 'Content-Type: application/json' -d '{"ip":"192.168.1.123","mac":"00:11:22:11:22:33","hostname":"test-dhcp-restapi.example.com"}'

For curl I’m not sure you can use & to pass in multiple items. I’ve always repeated --data. So you can try this:

curl -X POST https://foreman-smartproxy.example.com:8443/dhcp/192.168.1.0 --data hostname=test-dhcp-restapi.example.com --data ip=192.168.1.123 --data mac=00:11:22:11:22:33

If that doesn’t work I’d simply resort to adding print/log statements in various places to trace where it’s going wrong.

1 Like

Believe me or not, but my dhcp rest requests with its json content seems not being able to pass some http-server, middleware, validation code or what else to make its way to dhcp proxy api code. I copied/pasted the header you posted and I am always getting “invalid ip address” while the ip address is always empty.

I did as you said and put some debugging statements into the code:

D, [2019-07-06T23:01:15.927551 ] DEBUG -- : params: {"splat"=>[], "captures"=>["192.168.1.0"], "network"=>"192.168.1.0"}
D, [2019-07-06T23:01:15.927708 ] DEBUG -- : 
E, [2019-07-06T23:01:15.927983 ] ERROR -- : Invalid IP Address 
D, [2019-07-06T23:01:15.928016 ] DEBUG -- : Invalid IP Address  (Proxy::Validations::Error)
/usr/share/foreman-proxy/lib/proxy/validations.rb:19:in `validate_ip'
/usr/share/foreman-proxy/modules/dhcp_common/server.rb:132:in `add_record'
/usr/share/foreman-proxy/modules/dhcp_common/isc/omapi_provider.rb:29:in `add_record'
/usr/share/foreman-proxy/modules/dhcp/dhcp_api.rb:99:in `block in <class:DhcpApi>'
/usr/share/gems/gems/sinatra-1.4.8/lib/sinatra/base.rb:1611:in `call'

And as you can see IP Address is empty already in dhcp_api.rb (or maybe not ?, what is splat array ?). I dont want to further touch my productive server. I have to admit that I dont know anything about lib/sinatra/base.rb. So feel free to suggest some debugging/logging statements in sinatra lib or maybe protection/xss_header.rb. But please give me detailed instructions how to do, in order not to harm the server.

That could be explained by a JSON decode error. I’ll see about replicating it later.

Have you solved this?

No, I had no luck. But I havent tried anymore.

However, in our environment there was requirment to create host based on existing VM. Now, instead of letting the host create implicitely based on puppet-foreman interplay (as we did in the most recent past), we now just create the host via REST API with appropriate MAC/IP Settings. By this we circumvent the problem having to set the IP Address with the DHCP Rest Call.