a (hacky, wip) multi-tenant oidc-terminating reverse proxy, written in anger on top of pingora
at main 203 lines 6.1 kB view raw view rendered
1# An OIDC Reverse Proxy, Written in Anger 2 3## What is it? 4 5It's a multi-tentant non-caching tls-terminating [reverse proxy] that can 6terminate a hybrid of [OpenID Connect Core v1.0 with errata 2][oidc-v1] [^1] 7and [OAuth 2.1][oauth-v2.1] [^2]. It also generally reverse proxies HTTP 1.x, 8h2, websocket, and grpc traffic, as it's built (right now) on [pingora]. 9 10## ... what? 11 12You stick in front of webapps that need either tls added or don't support 13checking oauth2 state themselves, and it does that for you. 14 15Think [oauth2-proxy], but with proper multitenancy support and significantly 16less battle-tested. 17 18## whyyyy???? 19 20I [was](https://bsky.app/profile/directxman12.dev/post/3me6doan42k2t) [frustrated](https://bsky.app/profile/directxman12.dev/post/3merwp3aik22u) with the state of oauth2/oidc-terminating proxies. 21 22Particularly, at the time of writing, [oauth2-proxy] does not support 23multitenancy properly (it encourages the insecure practice of sharing 24client_ids if you use it for forward auth). 25 26Other things, like [caddy-security], felt overcomplicated for what i needed to 27do, and tbh i don't trust security tools that feel like they have everything 28and the the kitchen sink shoved in, unless they've been written by experts 29and/or extensively auditted. 30 31[caddy-security]: https://github.com/greenpau/caddy-security 32 33## how do i use it? 34 35The configuration is done in [textproto] format [^3]. 36The format is in <src/config/format.proto>, and the inline docs serve as the 37documentation, so i'd suggest starting there. 38 39Here's a quick example config: 40 41```textproto 42# bind on ipv6 43bind_to_tcp { 44 addr: '[::]:8443' 45} 46 47# serve these domains 48 49# serve copyparty w/ oidc termination on 50domains { 51 key: "files.example.com" 52 value: { 53 # serve to a (single, here, but this is a `repeated` field) 54 # uds backend, without tls 55 uds { 56 path: "/run/copyparty/party.sock" 57 } 58 # enable oidc termination on this domain 59 oidc_auth { 60 # standard oidc/oauth stuff 61 discovery_url_base: "https://sso.example.com/oauth2/openid/files/" 62 client_id: "files" 63 64 # this is where we redirect after logout 65 logout_url: "https://sso.example.com/" 66 # put your client secret here 67 client_secret_path: '/var/lib/secrets/files.client-secret' 68 69 # these scopes are required, and sent in the request 70 scopes { 71 required: "profile" 72 required: "group_names" 73 required: "party" 74 } 75 # these claims are mapped to headers 76 claims { 77 claim_to_header { 78 claim: "files_groups" 79 header: "X-Idp-Groups" 80 # since this is an array, concatenate it with commans 81 # before sending it to fileparty 82 serialize_as { 83 join_array_items_with: "," 84 } 85 } 86 claim_to_header { 87 claim: "name" 88 header: "X-Idp-User" 89 } 90 }, 91 } 92 93 # serve tls with the following certs 94 tls { 95 cert_path: "/var/lib/secrets/serving1.cert" 96 key_path: "/var/lib/secrets/serving1.key" 97 } 98 } 99} 100 101# serve your sso server, with oidc termination off 102domains { 103 key: "sso.example.com" 104 value: { 105 # your sso server might have it's own tls termination too, so 106 # connect to it with tls 107 https { 108 addr: "127.0.0.1:1309" 109 } 110 } 111 # serve tls with the following certs 112 tls { 113 cert_path: "/var/lib/secrets/serving2.cert" 114 key_path: "/var/lib/secrets/serving2.key" 115 } 116} 117``` 118 119[textproto]: https://protobuf.dev/reference/protobuf/textformat-spec/ 120 121[^3]: **Q**: wait, seriously? **A**: yes, seriously. i'm putting my money 122 where my mouth is, so to speak, when i say that textproto is nice, actually. 123 124## should i trust this? 125 126**_no, probably not._** 127 128consider the following points if you chose to run this: 129 130- i make no claims about it's security. 131 132- i'm not a security professional, just a dev with a hobby of writing oauth 133 implementations in anger, apparently [^4]. 134 135- if you're often targetted by script kiddies or nation-state actors, 136 or you're working for a company or organization, please don't use this. 137 138- it'll probably be fine for low-stress homelab situations? that's what i'm using 139 it for. 140 141[^4]: last year i wrote (but haven't published yet) a cute little oauth server 142 that uses exclusively passkeys from scratch. 143 144## but does it support... 145 146<dl> 147<dt>...serving forward auth?</dt> 148<dd> 149 150no, not right now. forward auth is an [underspecified mess][forward-auth-mess], 151so it's unlikely that i'll add support. 152 153</dd> 154 155<dt>...authorization?</dt> 156<dd> 157 158nope, this was written for reverse-proxying apps that either 159 160a) can make their own auth decisions (like [copyparty]), or 161b) don't need to make complex auth decisions 162 163</dd> 164 165<dt>...other types of authn?</dt> 166<dd> 167 168nope, and it's unlikely that it will -- i prefer to keep things well-scoped. 169unless it's something odic/oauth2-adjacent, in which case i'd consider it. 170 171</dd> 172 173</dl> 174 175For anything else, check the [issue tracker] and/or file an issue. 176 177## omg the code is messsyyyyyy! 178 179yeah, it probably needs further refactoring. 180 181## Contributing 182 183On the off-chance that you'd like to submit code, i'll take a look at it and 184review it, but i'm likely going to be picky about what new features i accept. 185it's probably worth filing an issue to discuss first. 186 187any code submitted should be your own, and you should understand it fully. 188 189[forward-auth-mess]: https://github.com/kanidm/kanidm/issues/2774 190[issue tracker]: https://tangled.org/directxman12.dev/proxy-in-anger/issues 191[copyparty]: https://github.com/9001/copyparty 192 193 194 195[reverse proxy]: https://en.wikipedia.org/wiki/Reverse_proxy 196[oidc-v1]: https://openid.net/specs/openid-connect-core-1_0.html 197[oauth-v2.1]: https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1 198[pingora]: https://github.com/cloudflare/pingora 199[oauth2-proxy]: https://github.com/oauth2-proxy/oauth2-proxy 200 201 202[^1]: the uuuh... reasonable parts? yeesh, there's a lotta junk in oidc core v1 203[^2]: specifically, the authorization code grant flow