Ngrok Tunnel: A more secure but simple to use alternative to port forwarding

I'm working on a plugin to create a simple but secure alternative to port forwarding.

Forwarding a port and setting up a dynamic dns record unfortunately remains a popular way to make an OctoPrint instance accessible through the internet. This is very insecure (especially when not using HTTPS with a proper certificate), and requires changes to the local network (static IP for the OctoPrint instance, configuration changes to the router). The truly safe way to access an OctoPrint instance is to use a VPN, but this is hard to set up and properly configure.

The OctoPrint Ngrok Tunnel plugin sets up a secure tunnel to your OctoPrint instance via the ngrok service. The tunnel is encrypted with SSL and proper certificates (even if your OctoPrint instance is not accessible via HTTPS locally), and is further protected with Basic Authentication (username and password) out of the box.

There is no configuration of the local network necessary, and there are no ports to open or forward. No certificates to set up. Just full access to your OctoPrint instance, including both the web interface and the API, from anywhere in the world. With the standard OctoPi confguration, you even get a working webcam stream remotely (if your home internet connection can keep up with the demands of streaming mjpeg).

You only need to sign up for a free account at ngrok.com. There is one caveat: with a free account, you get assigned a random subdomain of ngrok.com, and this subdomain changes each time OctoPrint gets restarted. The tunnel address is shown within the OctoPrint interface, but it can also be looked up in the ngrok service dashboard. With a paid ngrok account, you can configure a static subdomain to use. A paid plan gives you additional features, such as creating an IP whitelist to limit who can access the tunnel.

Installing the ngrok software, configuring and starting the tunnel is all done by the plugin. Before I publish an official release, I would like to get some feedback "from the field". The current prerelease version of the plugin can be found here:


It can be installed via the plugin manager by entering the following URL:

https://github.com/fieldOfView/OctoPrint-ngrok/archive/master.zip
1 Like

I'll give it a test, and let you know how it goes

2 Likes

Seems it's got a syntax error when OctoPrint starts:

Traceback (most recent call last):
  File "/home/pi/oprint/local/lib/python2.7/site-packages/octoprint/plugin/core.py", line 599, in _parse_metadata
    root = ast.parse(f.read(), filename=path)
  File "/usr/lib/python2.7/ast.py", line 37, in parse
    return compile(source, filename, mode, PyCF_ONLY_AST)
  File "/home/pi/oprint/local/lib/python2.7/site-packages/octoprint_ngrok/__init__.py", line 157
    "bind_tls":True,
              ^
SyntaxError: invalid syntax
1 Like

Same here...

2020-06-19 15:52:13,957 - octoprint.plugin.core - ERROR - Invalid syntax in /home/pi/oprint/local/lib/python2.7/site-packages/octoprint_ngrok/__init__.py for plugin ngrok
Traceback (most recent call last):
  File "/home/pi/oprint/local/lib/python2.7/site-packages/octoprint/plugin/core.py", line 599, in _parse_metadata
    root = ast.parse(f.read(), filename=path)
  File "/usr/lib/python2.7/ast.py", line 37, in parse
    return compile(source, filename, mode, PyCF_ONLY_AST)
  File "/home/pi/oprint/local/lib/python2.7/site-packages/octoprint_ngrok/__init__.py", line 157
    "bind_tls":True,
              ^
SyntaxError: invalid syntax
2020-06-19 15:52:13,990 - octoprint.plugin.core - INFO - Loading plugins from /home/pi/oprint/lib/python2.7/site-packages/octoprint/plugins, /home/pi/.octoprint/plugins and installed plugin packages...
2020-06-19 15:52:14,123 - octoprint.plugin.core - ERROR - Could not locate plugin SimplyPrint
Traceback (most recent call last):
  File "/home/pi/oprint/local/lib/python2.7/site-packages/octoprint/plugin/core.py", line 927, in _import_plugin_from_module
    location, spec = _find_module(module_name)
  File "/home/pi/oprint/local/lib/python2.7/site-packages/octoprint/plugin/core.py", line 64, in _find_module
    spec = imp.find_module(name)
ImportError: No module named octoprint_simplyprint
2020-06-19 15:52:14,144 - octoprint.plugin.core - ERROR - Invalid syntax in /home/pi/oprint/local/lib/python2.7/site-packages/octoprint_ngrok/__init__.py for plugin ngrok
Traceback (most recent call last):
  File "/home/pi/oprint/local/lib/python2.7/site-packages/octoprint/plugin/core.py", line 599, in _parse_metadata
    root = ast.parse(f.read(), filename=path)
  File "/usr/lib/python2.7/ast.py", line 37, in parse
    return compile(source, filename, mode, PyCF_ONLY_AST)
  File "/home/pi/oprint/local/lib/python2.7/site-packages/octoprint_ngrok/__init__.py", line 157
    "bind_tls":True,
              ^
SyntaxError: invalid syntax

same in python 3

2020-06-19 15:59:16,515 - octoprint.plugin.core - ERROR - Invalid syntax in /home/pi/oprint/lib/python3.7/site-packages/octoprint_ngrok/__init__.py for plugin ngrok
Traceback (most recent call last):
  File "/home/pi/oprint/lib/python3.7/site-packages/octoprint/plugin/core.py", line 599, in _parse_metadata
    root = ast.parse(f.read(), filename=path)
  File "/usr/lib/python3.7/ast.py", line 35, in parse
    return compile(source, filename, mode, PyCF_ONLY_AST)
  File "/home/pi/oprint/lib/python3.7/site-packages/octoprint_ngrok/__init__.py", line 157
    "bind_tls":True,
              ^
SyntaxError: invalid syntax
1 Like

Sorry, that should be fixed now.

1 Like

: instead of =?

1 Like

yeah... sigh. Why do I keep making minor cosmetic code changes just before I push an update?

1 Like

next error...

2020-06-19 16:02:49,628 - octoprint.plugin.core - ERROR - Invalid syntax in /home/pi/oprint/lib/python3.7/site-packages/octoprint_ngrok/__init__.py for plugin ngrok
Traceback (most recent call last):
  File "/home/pi/oprint/lib/python3.7/site-packages/octoprint/plugin/core.py", line 599, in _parse_metadata
    root = ast.parse(f.read(), filename=path)
  File "/usr/lib/python3.7/ast.py", line 35, in parse
    return compile(source, filename, mode, PyCF_ONLY_AST)
  File "/home/pi/oprint/lib/python3.7/site-packages/octoprint_ngrok/__init__.py", line 157
    "bind_tls"=True,
    ^
SyntaxError: keyword can't be an expression
1 Like

I think you don't need the quotes on the first part, should be dict(bind_tls=True, auth=auth_string)?

1 Like

...or maybe I should just go back to sleep. Can I do a "do-over" and can we pretend this did not happen? :wink:

As should be painfully obvious, I have not yet set up automated testing for the repo. I should.
Fixed properly now.

1 Like

May need to add some checks for errors. I tried connecting ngrok but the auth key didn't match your regex pattern match on the input. I still was able to save and create a tunnel, but then basic authentication wasn't working. Requested a new auth token and that one did match the pattern but now I get this error. Is there a way to end the previous tunnel?

2020-06-19 16:26:57,885 - pyngrok.process - ERROR - t=2020-06-19T16:26:57+0100 lvl=eror msg="failed to auth" obj=tunnels.session err="Your account 'Jim Neill' is limited to 1 simultaneous ngrok client session.\nActive ngrok client sessions in region 'us':\n  - ts_1dXnYX3GvsnkEsWUjiIrF37yfQN (73.122.180.167)\r\n\r\nERR_NGROK_108\r\n"

another recommendation, when the settings_plugin_ngrok_tunnel_address is blank make the button in the navbar open up the ngrok plugin's settings page (data-bind="click: function(){ if(tunnelName()=='') settingsViewModel.show('#settings_plugin_ngrok'); }") after adding a pararameters binding.

2 Likes

Yeah, can't seem to get basic auth to work at all. The tunnel is getting established but when I try to login it's not taking.

1 Like

Definitely needs a way to kill the tunnel, ended up having to disable the plugin and reboot the pi in order to stop it.

1 Like

The plugin should already kill the tunnel process when closing OctoPrint, but it is a good idea to add a way to close the tunnel without restarting OctoPrint.

2 Likes

If you had a basic account with NGROK, how does the plugin handle multiple connections/tunnels from different OctoPrint instances? This might come up for some users.

1 Like

I have pushed updates to add disconnect and connect buttons to the settings for the plugin. I have also made basic authentication optional for now, and removed the regex for the auth token. I have not yet added a user-facing message to show why the tunnel is not created if it fails.

Good question. It probably fails if you try to open another tunnel. What a user could do is set up a single reverse proxy (haproxy, nginx) for all of the instances, so you would get something like octoprint-proxy.local/instance1, octoprint-proxy.local/instance2 etc (much like how you access /webcam on the same port as octoprint in the default octopi configuration), and you could create one ngrok tunnel to that reverse proxy. I don't think you would be using this plugin at that point though. My main purpose for this plugin is to cater for people with one printer who want to easily and securely set up remote access.

1 Like

That was what I thought the intent of this was for, but know there are going to be people asking. After removing the previously saved authentication options the tunnel is successful and I'm able to connect.

Edit: it might be nice to make the navbar button toggle the connect/disconnect if their is an API key entered with css color indicators of status, otherwise open settings and make it optional to auto connect on startup. Just adding some ideas, not really necessary.

Edit 2: make Ngrok dashboard link open in new tab

2 Likes

Thanks for all the suggestions. I take it the tunnel works now?

The navbar icon is now a dropdown menu:
image
If only I knew why it aligned with the wrong navbar item though...

2 Likes

Yeah, I had the same problem with my MQTTPublish plugin. I think what eventually resolved it for me was switching from a UL tag to just a DIV for the dropdown-menu class.

Nope, I found it; you have to register the class for the navbar template: