a (hacky, wip) multi-tenant oidc-terminating reverse proxy, written in anger on top of pingora
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