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

Nice, thanks for sharing. I was trying to use the same approach for Sticky Pad plugin, but ended up just creating a whole separate view model.

I still haven't figured out why basic auth doesn't work. Are there any requirements you know of for the username/password or is that possibly a paid feature?

I think I have fixed your basic auth problem. I was being overzealous with escaping special characters in the username and password. I have also made creating the tunnel on start optional.

I'm inching towards a first release, but I want to add some user-facing messages in cases where the tunnel fails to open (eg trying to open more than one tunnel on a single free ngrok account).

The "master" version of the plugin now shows messages when the tunnel fails to be created. I'll do an initial official release tomorrow (unless unexpected issues pop up; with my track-record...).

What I could do in a future version of the plugin is let one instance act as a tunnel server, and let multiple other instance register themselves. That way, up to 4 instances can be supported on the free ngrok plan, and up to 8 on the $5/month plan. I expect people who have bigger farms to be able to set up their own infrastructure.

1 Like

Yep, authentication seems to be working now and error messages are working as previous tunnel still existed on reinstall and for some reason the tunnel wasn't closed during restart. Rebooting the Pi got it all squared away and it seems to be working.

I'm wondering if I should make basic auth required, like I had it originally. Without it, you miss an important chunk of security. If we could trust users to make the right decision, this plugin would not be necessary.

I'm strongly leaning towards making it required. Otherwise you are barely more secure than with a simple port forward.

edit Just realized... if the basic auth headers actually get passed through to the OctoPrint instance by ngrok, you could even put a settings overlay into the plugin to have OctoPrint trust the supplied user name and default to the current (admin) username when setting up authentication on the tunnel. There's config settings for that in OctoPrint for http basic auth setups, though they are only available through config.yaml and not via the UI.

2 Likes

We know ways to handle that...ie Tab Order plugin...

That part was easy:

And it works as expected. You could have used self._settings.global_get_boolean() and self._settings.global_set_boolean too BTW...

1 Like

Any tips on getting get_settings_preprocessors to work?

I don't like storing the password in the settings in plain text. I would like to at least obfuscate it.

Small heads-up. If you trust basic auth, you either must make sure that it only uses the user name of the currently logged in user when configuring auth on the tunnel, or you need to make sure it also checks the password (and instruct the user that both should match), so accessControl.checkBasicAuthenticationPassword needs to be true too. It should be that by default, but it could have been changed manually.

Otherwise someone could in theory use the ngrok tunnel to authenticate as another admin user (by setting the http auth name in ngrok to the username of the other user and then accessing the instance through the tunnel). Not a huge security issue considering that you already need to be admin and thus able to pretty much nuke the whole instance in order to set up a tunnel, but it's still bad form IMHO.

And like @jneilliii said, instead of that settings() construct there you really should use self._settings.get_global_boolean and self._settings.set_global_boolean :wink:

I have never used that one before, so not really sure.

Oops, sorry, overlooked that. Does the example not work?

def get_settings_preprocessors(self):
    return dict(auth_pass=lambda x: deobfuscate(x)),
           dict(auth_pass=lambda x: obfuscate(x))
1 Like

No, not even this works:

	def get_settings_preprocessors(self):
		return dict(auth_pass=lambda x: x+"deobfuscated"), dict(auth_pass=lambda x: x+"obfuscated")

Perhaps my expectations are incorrect. With the above code, I expect my auth_pass setting to save into config.yaml with the string "obfuscated" appended to it. And when getting the same setting, I expect it to end in obfuscateddeobfuscated. But neither is happening.

Sounds like something is broken in OctoPrint then, that should definitely work like that. Can you open a ticket so I don't forget about that? I'm about to call it a day over here, just trying to get a build to work.

I probably confused OctoPrint with my use of settings().get(). I think I got it working now.

switching those you don't need your import anymore...

The plugin is now available via the plugin manager.: Ngrok Tunnel

Many thanks to @jneilliii and @foosel for their tips and advice.

2 Likes