1

TL;DR: How to authenticate against NGINX if auth headers are not supported on client side?

I am building an IoT-related project using NGINX as a reverse proxy for the server side services and 1NCE as the LTE carrier for the mobile devices. All traffic is authenticated based on HTTPBasicAuth over SSL-encrypted connections and handling "normal" requests works as desired.

As mobile service might be interrupted and the Internet connection might be lost, I want to send SMS for critical status reports and alarm notifications. 1NCE supports SMS mobile originated SMS (MO SMS) which are handled by the 1NCE's internal infrastructure and forwarded to a configurable API endpoint. So, MO SMS are not delivered to a specified phone, but forwarded via an API request which I need to process on my side.

According to 1NCE's SMS documentation and in consultation with their customer support, SMS forwarding does not support any authentication headers. SMS forwarding can only be done by specifying an HTTPS URL (including the desired API endpoint) and a port. The incoming SMS is then wrapped in a request to the given URL and sent in the request body.

I want to add authentication to the SMS forwarding endpoint (receiving forwarded SMS on my side) as well and am currently wondering about how I could achieve this. NGINX supports authentication on subrequest which could be used to evaluate incoming requests by an internal service. So my first idea was to add some credentials to each SMS (as I am also responsible for the SMS sending part of the code on the mobile devices, I could implement whatever is needed) and check those credentials with an internal service called by NGINX's subrequest. However, this does not seem to be doable. According to this SO question GET requests are used for the internal subrequests hence any body of the incoming POST request is discarded. Therefore, the credentials of the forwarded SMS would also be not available to my internal auth service. Extending NGINX's auth capabilities by writing an custom Lua-based plugin was my second idea, but this does not only seem to be not feasible but is also not supported by the NGINX instance I am using (Lua modules are disabled, switching to openresty seems to be a big thing).

My last idea would be to forward all incoming requests to a Python web service (written in Flask, other services I am using are also written in Flask) and parsing the forwarded SMS in Python. Based on the result of the credential evaluation I could return an 401/Unauthorized status code if credentials provided in the SMS (which is part of the request body) are invalid and process the request otherwise. However, I think that this approach is quite ugly as all incoming requests need to be passed to Flask and invalid requests are not rejected at the level of my Reverse Proxy.

Do you have any ideas about how to approach this issue? What would be a considerable approach with regards to "best practises"? Can I extend NGINX in a way to solve this or should I completely drop NGINX in favor of a "better" proxy?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
albert
  • 8,027
  • 10
  • 48
  • 84
  • This is one of those times where I'm absolutely livid with the folks who got rid of the authentication part of the URL. You could probably do https://user:pass@example.com, and it might even work today, but due to overly broad security concerns in userland you'll have to actually put your authentication in the URL somehow. I wonder if there's a crafty way you can use the URL rewriting to move a request parameter to a request header? – Brad Mar 20 '21 at 22:05
  • Do the requests all come from 1NCE servers? Can't you just whitelist their IP ranges and deny all others? – miknik Mar 22 '21 at 02:14
  • @miknik: I thought about that (but did not mention it in my question). That would require denying access to a specific API endpoint (NGINX location) which is possible and might be considered a valid way. However, 1NCE was not able to verify that their SMS forwarding is done by a constant set of IP addresses. – albert Mar 22 '21 at 10:23
  • 1
    Well they won't have unlimited IP addresses. Alternatively you could use the auth request module and pass the client IP to your authentication script, have that look it up and if it belongs to 1NCE authorise the request and if not then deny it. – miknik Mar 24 '21 at 21:29

0 Answers0