Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Request: LUA Records - allow test IP to be different than returned IP #7468

Open
splk3 opened this issue Feb 8, 2019 · 11 comments
Open

Comments

@splk3
Copy link

splk3 commented Feb 8, 2019

  • Program: Authoritative
  • Issue type: Feature request

Short description

Allow the test IP in a LUA record to be different than the returned answer. Existing tests return an answer that is one of the tested IPs, it would be useful to test against one IP, and return an entirely different IP (such as testing an internal address, and returning the outside NAT address).

Usecase

For some systems (especially older/legacy), it would be useful to be able to test a different IP than the one returned. For example, if a system does not have a good health-check function/API/URL, the admin web interface might be the best way to detect UP/DOWN health. In this case, it is preferred to keep the admin interface available on the inside address only, and not available from the outside network.

This is very common in other GSLB solutions, but is, admittedly, not the best way to test in most circumstances. However, it would provide a great deal of flexibility in letting admins craft the best health checks possible given their existing systems and infrastructure.

Description

There are (at least) two possible ways to enable this functionality. They are defined in Option A and Option B below.

Option A - Add an optional Test IP field

One option is to add an optional field for the address used to test against, in addition to the answer it will return.

For example, with the ifportup test, an example test might be:

A "ifportup(80, {'192.168.1.100', '192.168.1.101'})"

This will check to see if port 80 is listening on 192.168.1.100 and 192.168.1.101, and return one of the healthy IPs. A different syntax could indicate a different test IP, which would be optional. The implementation isn't important, but for the sake of discussion, a separator could be used between the answer-ip and the test-ip. An example is below using a + to separate the two IPs, with the following test parameters. This is just an example; the syntax is not important, and can be changed to fit the limitations of the LUA and/or other requirements/limitations, as needed.

  • check if port 80 is listening at 192.168.2.100, and mark answer 192.168.1.100 as HEALTHY if test is successful
  • check if port 80 is listening at 192.168.2.101, and mark answer 192.168.1.101 as HEALTHY if test is successful
  • return one of the HEALTHY answers
A "ifportup(80, {'192.168.1.100+192.168.2.100', '192.168.1.101+192.168.2.101'})"

This can be expanded upon for other tests, with the most obvious one the ifurlup() function.

Option B - Add a true/false flag to LUA test functions

What is perhaps a better option would be to allow functions to have a true/false flag that, if set, simply returns true or false if the test is successful. Then the administrator can configure the LUA record in a fashion similar to the netmask() source address function.

For example, the example LUA entry below uses the netmask() function to determine which CNAME to return:

cname "; if(netmask({192.168.1.1/24})) then return 'server.office.lan' else return 'server.company.com'"

If an optional argument was added to the end of the end of the passed set of values, such as tf, the test would return a true or false instead of one of the healthy member's information. Then, using LUA, decisions can be made about how to answer the query. The below example would allow similar functionality but using a URL check as the boolean test case for the if/then tree.

cname "; if(ifurlup('https://service.company.com',{'192.168.1.100'}, {tf='true'}) then return 'service.company.com' else return 'errorpage.company.com'"
`` 
@Habbie
Copy link
Member

Habbie commented Jul 13, 2019

A "ifportup(80, {'192.168.1.100+192.168.2.100', '192.168.1.101+192.168.2.101'})"

Untested: A "({['192.168.2.100']='192.168.1.100', ['192.168.2.101']='192.168.1.101'})[ifportup(80, {'192.168.2.100', '192.168.2.101'})

@Habbie
Copy link
Member

Habbie commented Jul 13, 2019

Untested: A "({['192.168.2.100']='192.168.1.100', ['192.168.2.101']='192.168.1.101'})[ifportup(80, {'192.168.2.100', '192.168.2.101'})

I assumed some oversimplifications, it may not be this easy in fact...

@Habbie
Copy link
Member

Habbie commented Jul 13, 2019

ifportup returns an array of strings, you could map those in the suggested way, as a Lua hack for your option A.

Option B - Add a true/false flag to LUA test functions

Right now you can kind-of hack this in by having a fully reliable but useless fallback IP (like localhost) and testing for that in the ifportup/ifurlup return value, making sure you never return it to the world. I agree it would be nice if this was easier.

@Habbie Habbie modified the milestones: auth-4.2.0, auth-4.2.x, auth-4.3.0 Jul 17, 2019
@splk3
Copy link
Author

splk3 commented Jul 24, 2019

I will test option A using the hack you provided and get back to you with my results. I hadn't thought of the different ways the functions handle values/vars vs. strings. Thanks for the assistance.

@splk3
Copy link
Author

splk3 commented Aug 23, 2019

I'm using the geoip backend for simplicity, and in the yaml zone file, I have the following entry just to test with 1 upstream server to see if I can get it to return a different IP than the one used for testing.

test.gslb.example.com:
  - lua: 'a "({[''192.168.1.100'']=''192.168.2.100''})
    [ifurlup(''https://test.example.com:8443/login/'', {''192.168.1.100''})]"' 

I can see the status update as follows for the health check:

pdns_server: LUA record monitoring declaring 192.168.1.100 UP for URL
https://test.example.com:8443/login/!

but I don't get a DNS answer. Instead there is an error message on the server:

Lua record reported: Trying to cast a lua variable from "nil" to
"N5boost7variantISsSt6vectorISt4pairIiSsESaIS3_EENS_6detail7variant5void_ES8_S8_S8_S8_S8_S8_S8_S8_S8_S8_S8_S8_S8_S8_S8_S8_S8_EE"

@cmouse
Copy link
Contributor

cmouse commented Aug 23, 2019

I can reproduce the problem, and what @Habbie suggested didn't work either even after syntax fixes.

@chbruyand
Copy link
Member

I think adding portup() and urlup() calls would be cleaner ?

@chbruyand
Copy link
Member

test.gslb.example.com:
  - lua: 'a "({[''192.168.1.100'']=''192.168.2.100''})
    [ifurlup(''https://test.example.com:8443/login/'', {''192.168.1.100''})]"' 

ifurlup() returns an array of strings (the results). Something like the following should be more appropriate

test.gslb.example.com    3600    IN      LUA     A ";local upurls = ifurlup('https://test.example.com:8443/login/', {'192.168.1.100'});"                                                                                                                               
                                                  " local replacement_table = {['192.168.1.100']='192.168.2.100'};                    "                                                                                                                               
                                                  " local ret = {};                                                                   "                                                                                                                               
                                                  " for key,value in pairs(upurls) do table.insert(ret, replacement_table[value]) end;"                                                                                                                               
                                                  " return ret;                                                                       "

@splk3
Copy link
Author

splk3 commented Aug 27, 2019

@chbruyand - used a variation of your suggestion above with success!! thank you everyone who chimed in - I'm seeing some of the benefits of using a generic scripting engine instead of a static set of health checks!
on a side note, you can use table.concat(var) to turn a list into a string to do a quick test against two lists of IP addresses (only tested with a list of a single IP). I played with that a little just to be able to do direct comparison of lists, but this is much more flexible and the way I will go forward.

@chbruyand
Copy link
Member

Beware that table.concat() may not be available in next major version if we default to strict sandboxing.

@splk3
Copy link
Author

splk3 commented Aug 30, 2019

Good to know - I didn't use it because your suggested solution is much more flexible, so no reason to use a single-use-case solution when a more generic one exists. Thanks again!

@Habbie Habbie modified the milestones: auth-4.3.0, auth-4.4.0 Jan 21, 2020
@Habbie Habbie modified the milestones: auth-4.4.0, auth-5 Nov 9, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants