a (hacky, wip) multi-tenant oidc-terminating reverse proxy, written in anger on top of pingora
Rust 96.8%
Nix 3.2%
14 2 0

Clone this repository

https://tangled.org/directxman12.dev/proxy-in-anger https://tangled.org/did:plc:3nei4lvpwagyfqizd4mqyyko/proxy-in-anger
git@knot.tangled.wizardry.systems:directxman12.dev/proxy-in-anger git@knot.tangled.wizardry.systems:did:plc:3nei4lvpwagyfqizd4mqyyko/proxy-in-anger

For self-hosted knots, clone URLs may differ based on your setup.

Download tar.gz
README.md

An OIDC Reverse Proxy, Written in Anger#

What is it?#

It's a multi-tentant non-caching tls-terminating reverse proxy that can terminate a hybrid of OpenID Connect Core v1.0 with errata 2 1 and OAuth 2.1 2. It also generally reverse proxies HTTP 1.x, h2, websocket, and grpc traffic, as it's built (right now) on pingora.

... what?#

You stick in front of webapps that need either tls added or don't support checking oauth2 state themselves, and it does that for you.

Think oauth2-proxy, but with proper multitenancy support and significantly less battle-tested.

whyyyy????#

I was frustrated with the state of oauth2/oidc-terminating proxies.

Particularly, at the time of writing, oauth2-proxy does not support multitenancy properly (it encourages the insecure practice of sharing client_ids if you use it for forward auth).

Other things, like caddy-security, felt overcomplicated for what i needed to do, and tbh i don't trust security tools that feel like they have everything and the the kitchen sink shoved in, unless they've been written by experts and/or extensively auditted.

how do i use it?#

The configuration is done in textproto format 3. The format is in <src/config/format.proto>, and the inline docs serve as the documentation, so i'd suggest starting there.

Here's a quick example config:

# bind on ipv6
bind_to_tcp {
	addr: '[::]:8443'
}

# serve these domains

# serve copyparty w/ oidc termination on
domains {
	key: "files.example.com"
	value: {
        # serve to a (single, here, but this is a `repeated` field)
        # uds backend, without tls
		uds {
			path: "/run/copyparty/party.sock"
		}
        # enable oidc termination on this domain
		oidc_auth {
            # standard oidc/oauth stuff
			discovery_url_base: "https://sso.example.com/oauth2/openid/files/"
			client_id: "files"

            # this is where we redirect after logout
			logout_url: "https://sso.example.com/"
            # put your client secret here
			client_secret_path: '/var/lib/secrets/files.client-secret'

            # these scopes are required, and sent in the request
			scopes {
				required: "profile"
				required: "group_names"
				required: "party"
			}
            # these claims are mapped to headers
			claims {
				claim_to_header {
					claim: "files_groups"
					header: "X-Idp-Groups"
                    # since this is an array, concatenate it with commans
                    # before sending it to fileparty
                    serialize_as {
                        join_array_items_with: ","
                    }
				}
				claim_to_header {
					claim: "name"
					header: "X-Idp-User"
				}
			},
		}

        # serve tls with the following certs
        tls {
            cert_path: "/var/lib/secrets/serving1.cert"
            key_path: "/var/lib/secrets/serving1.key"
        }
	}
}

# serve your sso server, with oidc termination off
domains {
    key: "sso.example.com"
    value: {
        # your sso server might have it's own tls termination too, so
        # connect to it with tls
        https {
            addr: "127.0.0.1:1309"
        }
    }
    # serve tls with the following certs
    tls {
        cert_path: "/var/lib/secrets/serving2.cert"
        key_path: "/var/lib/secrets/serving2.key"
    }
}

should i trust this?#

no, probably not.

consider the following points if you chose to run this:

  • i make no claims about it's security.

  • i'm not a security professional, just a dev with a hobby of writing oauth implementations in anger, apparently 4.

  • if you're often targetted by script kiddies or nation-state actors, or you're working for a company or organization, please don't use this.

  • it'll probably be fine for low-stress homelab situations? that's what i'm using it for.

but does it support...#

...serving forward auth?

no, not right now. forward auth is an underspecified mess, so it's unlikely that i'll add support.

...authorization?

nope, this was written for reverse-proxying apps that either

a) can make their own auth decisions (like copyparty), or b) don't need to make complex auth decisions

...other types of authn?

nope, and it's unlikely that it will -- i prefer to keep things well-scoped. unless it's something odic/oauth2-adjacent, in which case i'd consider it.

For anything else, check the issue tracker and/or file an issue.

omg the code is messsyyyyyy!#

yeah, it probably needs further refactoring.

Contributing#

On the off-chance that you'd like to submit code, i'll take a look at it and review it, but i'm likely going to be picky about what new features i accept. it's probably worth filing an issue to discuss first.

any code submitted should be your own, and you should understand it fully.


  1. the uuuh... reasonable parts? yeesh, there's a lotta junk in oidc core v1 ↩︎

  2. specifically, the authorization code grant flow ↩︎

  3. Q: wait, seriously? A: yes, seriously. i'm putting my money where my mouth is, so to speak, when i say that textproto is nice, actually. ↩︎

  4. last year i wrote (but haven't published yet) a cute little oauth server that uses exclusively passkeys from scratch. ↩︎