How can we help?


URL Base

Comments

18 comments

  • Official comment
    Avatar
    RainMachine Support Agent

    Thanks for the input, its greatly appreciated.

     

    Brandon M
    RainMachine. 

    Comment actions Permalink
  • Avatar
    RainMachine Nicholas

    Hi Mark,

     

    I think you want a remote access to the device outside of our remote access solution ?

    If so this feature already exists, you can go to your mobile app Devices screen:

     

    1. Press the 3 lines (hamburger) menu from the top left corner

    2. Select Network Settings->Direct Access

    3. Add the https://< IP >: < PORT> combination that you want to access.

     

    Regards,

    Nicholas

     

    0
    Comment actions Permalink
  • Avatar
    Mark

    I host multiple apps on my network that I make accessible externally.  From a security perspective I only want one port opened externally.  Leveraging a reverse proxy allows me to do so.  My external URL for rainmachine is www.mydomain.net:443/rainmachine.  I can use nginx to strip off rainmachine and redirect it to my internal rainmachine IP and port number (https://192.168.1.5:8080), but it fails.  There seems to be a redirect at that address for the restful API as hitting it frm a browser goes /api/4/ and the api login page.  I can append that to the URL with nginx and redirect external traffic to https://192.168.1.5:8080/api/4/.  From a browser externally hitting https://www.mydomain.net/rainmachine I can get to the restful api main page.  Login however from a browser or the mobile app fails.  I am guessing all of the pages after logon are not in the /api/4/ folder or subfolders.  I could tell nginx to try different locations if I knew the folder structure for the web site and restful api.  All of the other apps I host internally all support a URL base so that they would listen on in this example https://192.168.1.5:8080/rainmachine.  In that scenario all one has to do in nginx is pass the external request to https://192.168.1.5:8080/rainmachine.  You don't have to strip off rainmachine, add /api/4/ and try to redirect to other folders needed for the web page and app to function properly.  A URL base simplifies this greatly and helps keep the environment more secure.

    0
    Comment actions Permalink
  • Avatar
    RainMachine Nicholas

     

    I'm not sure atm why it doesn't work, we have a similar functionality on our https://my.rainmachine.com. The api/4/ pages are not static but generated from our internal rest server.

    There is indeed a redirect internally on device from https://ip:8080 to http://locahost:18080 so we offload https handling. Maybe your issues are with POST (something in the redirect chain alters headers/body ?) Does GET works for https://www.mydomain.net/rainmachine/api/4/apiVer ? 

     

    Regards,

    Nicholas

    0
    Comment actions Permalink
  • Avatar
    Mark

    I had to add the redirect to my proxy_pass to get anything to work so my external URL is  https://www.mydomain.net/rainmachine which gets redirected behind the scenes to https://192.168.1.2:8080/api/4/.  When I add apiVER to  https://www.mydomain.net/rainmachine making it  https://www.mydomain.net/rainmachine/apiVER it gets redirected behind the scenes to https://192.168.1.2:8080/api/4/apiVER/.  I do not get an error in the browser, but a blank page is loaded.  I would guess that pages needs content outside of /api/4/ which it cannot reach.  The proxy_pass command makes the root of the web page be /api/4/.  So it if needs files say at /api/3/ or /etc/content/ it does not know how to get to them.  Having the ability to set the URL base then makes the reverse proxy base the same as the base for the web site on the appliance and then it knows how to reach them.

    0
    Comment actions Permalink
  • Avatar
    RainMachine Nicholas

    No there aren't any other "content" outside of /api/4/ even our /apiVer it's a reply from /api/4/apiVer (note there is not / at the end which might add to your errors) the /api/4/ is the URL base for all version 4.x API calls and don't depend on any other paths.

    You could try to redirect to http://192.168.1.2:18080/api/4/

     

    Regards,

    Nicholas

    0
    Comment actions Permalink
  • Avatar
    Mark

    I removed the trailing slash after apiVER and still get just a blank page.  I also had already tried to redirect to http://192.168.1.122:18080/api/4/ and get the same results.  I can get to the API login page in a browser and from the mobile app, but can get no further.

    0
    Comment actions Permalink
  • Avatar
    Mark

    I temporarily opened port 8080 on my firewall and forwarded it to 192.168.1.122:8080.  Now if I point the ios app at www.mydomain.net:8080 it works fine.  Pointing a browser at www.mydomain.net:8080/api/4/apiVER loads a blank page.  That seems to be normal behavior for that page.  Get the same result against 192.168.1.122:8080/api/4/apiVER and http://192.168.1.122:18080/api/4/apiVER.

    0
    Comment actions Permalink
  • Avatar
    RainMachine Nicholas

    it's apiVer only V upper case.

     

    Regards,

    Nicholas

    0
    Comment actions Permalink
  • Avatar
    Mark

    https://www.mydomain.net:8080/api/4/apiVer returns {"apiVer": "4.2.0", "hwVer": 3, "swVer": "4.0.750"}.

     

    https://www.mydomain.net/rainmachine/apiVer loads a blank page.  Does apiVer need something in say api/4/apiBin or something similiar and if so it is referenced via that absolute path or is it referenced via the relative path ../apiBin?  The absolute path would fail behind nginx.  The browser will return /rainmachine/apiBin (rainmachine effectively equals /api/4/) where as with an absolute reference the code is expecting rainmachine/api/4/apiBin.

    0
    Comment actions Permalink
  • Avatar
    RainMachine Nicholas

    No, there is no redirect or other paths being rerouted. The only redirect is that from https to http, the http rest server replies to all requests doesn't need to do any other routing or reference another path.

    0
    Comment actions Permalink
  • Avatar
    Mark

    Are code references absolute or reference based?

    0
    Comment actions Permalink
  • Avatar
    RainMachine Nicholas

    Code only understands absolute paths.

    0
    Comment actions Permalink
  • Avatar
    Mark

    So I think the only way to get it to work would be to change the code to use relative paths or proviude a URL base option.  I don;t think that issue can be worked around with a reverse proxy.

    0
    Comment actions Permalink
  • Avatar
    Mark

    For anyone else who stumbles across this and wants to give it a shot.

     

    This works for /api/4/

    location /rainmachine/ {
      rewrite /rainmachine/([^/]+) /$1 break;
      proxy_pass https://192.168.1.122:8080/api/4/;
    }

    This works for /api/4/apiVer

    location /rainmachine/ {
      rewrite /rainmachine/([^/]+) /api/4/$1 break;
      proxy_pass https://192.168.1.122:8080/;
    }

     

    Cannot seem to find a combination that will overcome the absolute pathing in the code and allow both to work without a URL base.

    0
    Comment actions Permalink
  • Avatar
    Jesse Goodier

    Just got this working for me:
    https://gist.github.com/jessegoodier/762563ce3b8c4b9715addafecb5501ca


    server {
    listen443ssl http2;
    server_name rainmachine.xyz;;

    ssl_certificate /etc/nginx/fullchain1.pem;
    ssl_certificate_key /etc/nginx/privkey1.pem;

    location / {

    proxy_passhttp://rainmachine-host$request_uri;

    proxy_http_version 1.1;
    proxy_cache_bypass $http_upgrade;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Server $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_ignore_client_abort on;

    }
    }
    upstream rainmachine-host {
    server10.0.1.121:8081;
    }
    0
    Comment actions Permalink
  • Avatar
    Mike G

    I'm looking for a similar setup as @Mark.

    I have several services that sit on my network that need to be proxied through NGINX for SSL and authentication. Each service gets a separate "root route", ex https://<mydomain>/octoprint, https://<mydomain>/home-assistant. I would like to have the ability to add a header to the upstream request to specify the root of all requests by the RainMachine instance.

    In the case of Octoprint, I can specify this:

    proxy_set_header X-Script-Name          /octoprint/artistd;

    This tells the web application to prefix all of its requests to its resources with "/octoprint/artistd".

    This could be detected and a "base" (https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base) element could be added.

    Looking at the unminified JavaScript it appears that all the paths are using an absolute reference:

      this.URL = Object.freeze({
        auth: '/auth',
        provision: '/provision',
        dailystats: '/dailystats',
        restrictions: '/restrictions',
        program: '/program',
        zone: '/zone',
        watering: '/watering',
        parser: '/parser',
        mixer: '/mixer',
        diag: '/diag',
        machine: '/machine',
        dev: '/dev',
        communityWeather: 'https://raw.githubusercontent.com/sprinkler/rainmachine-developer-resources/master/version-metadata.json'
      });

    Some simple JavaScript could be written to use window.location along with those paths to make them relative to the application root.

    For a work around I'm attempting at using the sub_filter module in NGINX to rewrite the JavaScript returned from the server. I haven't had luck yet. I'll post if I'm able to find a workaround.

    0
    Comment actions Permalink
  • Avatar
    Mike G

    Ok, update. I figured it out. The base element won't work because the script is using fully qualified URLs for requests as opposed to "/api/4". I found the following in code (unminified):

    var c = window.location.protocol + '//' + window.location.hostname + ':' + window.location.port + '/api/4'

    I get upset with my developers when they do this. Can't use this in multi-tenant situations, etc.

    So here's how I got around it.

    First create your base location for "rainmachine":

            location /rainmachine/ {
                    proxy_pass http://192.168.2.98:8081/ui/;
                    sub_filter '/api/' '/rainmachine/api/';
                  sub_filter_once off;
                    sub_filter_types *;

                    proxy_set_header Host                   $host;
                    proxy_redirect http:// https://;
                    proxy_set_header X-Forwarded-Host       $host;
                    proxy_set_header X-Real-IP              $remote_addr;
                    proxy_set_header X-Forwarded-For        $proxy_add_x_forwarded_for;

                    proxy_http_version                      1.1;
                    proxy_set_header Upgrade                $http_upgrade;
                    proxy_set_header Connection             "upgrade";
                    proxy_read_timeout                      86400;
            }

    The key here is the sub_filter* entries. This tell NGINX to modify the response so that "/api/" becomes "/rainmachine/api/". This effectively changes the JS so that it will send requests to the rainmachine location. Also note that I used the /ui/ route on my proxy_pass. This is so that my URLs look like https://<mydomain>/rainmachine/ in the browser.

    I then created one more location:

            location /rainmachine/api/ {
                    proxy_pass http://192.168.2.98:8081/api/;

                    proxy_set_header Host                   $host;
                    proxy_redirect http:// https://;
                    proxy_set_header X-Forwarded-Host       $host;
                    proxy_set_header X-Real-IP              $remote_addr;
                    proxy_set_header X-Forwarded-For        $proxy_add_x_forwarded_for;

                    proxy_http_version                      1.1;
                    proxy_set_header Upgrade                $http_upgrade;
                    proxy_set_header Connection             "upgrade";
                    proxy_read_timeout                      86400;
            }

    This captures the "/api" requests and sends them to the correct route on the upstream host. If you set your proxy_pass to be the root of the rainmachine server this may not be necessary.

    I'm still testing to see if everything works correctly. I've been able to log in successfully and browse around.

    To RainMachine: Please implement a setting so that this isn't necessary! I understand you guys want people on your service but there are some of us that are completely fine with this type of setup. I'm going to be migrating a lot of my internal servers to AWS soon with a VPN link locally so that I can dynamically update DNS records and have automated LetsEncrypt cert updates. Being able to change how the upstreams are accessed would be SUPER helpful. A secondary request would be to have a containerized instance of the UI so that I could deploy that to my k8s host and point that back to a "headless" RainMachine instance would be AWESOME!

    Thanks!

    Mike G

    0
    Comment actions Permalink

Please sign in to leave a comment.