Writing a plugin for my custom remote access setup(oidc w/ reverse proxy)

Hi Community,

I'm mainly a Java developer(specialized in Spring Boot and have written commercial fullstack web applications that use openid connect, vue, react, etc...) and have quite the experience with Linux but I'm not really familiar with Python but can probably manage(already setup a base plugin using cookiecutter and using PyCharm as IDE), anyways. I've setup my octoprint/octopi and run a reverse proxy to it on a remote vps with a domain name. The deployment diagram can be seen in the image below(excuse me for my half-baked UML skills)

I know I can use an existing cloud solution to remotely access my printer but I decided to setup my own as I'm more of an IT-guy. Everything's secured safely using ssh keys, firewalls, etc... I know it's probably really over-engineered but I found this to be a fun little side project next to my work.

If you try to access the ip/hostname of the octopi locally you'll get redirected to the domain of the vps(because of the custom HAProxy ACL config). The VPS then uses NGINX auth_request together with Vouch-Proxy and Keycloak to authenticate the user and create a keycloak session cookie. In my case I've setup GitHub as an identity provider for my Keycloak realm(named octoprint) so I can login with my Github account. The authenticated keycloak user's email is passed to the octoprint instance by the Vouch Proxy by adding a X-Vouch-User header(together with some other auth headers) used by the Octoprint ACL config in config.yaml. This config is set to trust remote users and not create new remote users to prevent other github users from accessing my printer incase keycloak messes up(I've only set it up for my own GitHub account and the ufw firewall on my octopi only allows traffic from within my LAN and the VPS' IP, user registration is also disabled on keycloak).

I can now safely access my printer from anywhere using oauth2(instead of the recommended basic auth). I know there's an Octoprint Github Oauth2 plugin but it does not work anymore/didn't meet my requirements.

Now come the development questions/advice requests:

  • Keycloak creates a separate session cookie. If I press logout on octoprint it wont actually log me out due to the separate cookie from Keycloak logging me back in. I assume the logout button action is passed down to the configured UserManager so I can delete the cookies from there(or do I need to hook into the frontend to replace the logout button?).
  • The bundled LoginUI plugin is still enabled but is no longer needed. I can probably disable it in config.yaml if I am correct(have not tried yet)?.
  • The session cookie Keycloak creates expires after 6 hours/a set period of time causing issues with authentication.

If I am correct I need to create a plugin which contains a custom UserManager to handle authentication through the keycloak session cookie instead of the Session_P443 cookie (UserManager would extend FileUserManager and validate the keycloak jwt cookie). This would be done by using the user_manager_factory_hook I assume. Or do I need to disable the creation of the Session_P443 cookie completely and hook into access_validator_hook and authenticate the jwt there?

I would also need to hook in to the acl_keyvalidator_hook and modify some nginx configurations on my vps to allow api access I assume(Currently requires the keycloak cookie due to my location/proxy_pass config. I'll probably keep using the existing octoprint API key mechanism but maybe later change to an oauth2 mechanism).

Current plan is to keep using the existing GroupManager and later switch to Keycloak and use the roles/user settings from there using a custom GroupManager.

My current development setup is PyCharm, Python 3.8 and I have my pip/virtualenv setup according to the plugin development guide. Pretty new to python so it'll take some time but I'll surely publish a tutorial/blogpost about my setup once it's all finished if there's demand for it.

Anything I'm missing? Any advice on what python libraries to use for this purpose?