a (hacky, wip) multi-tenant oidc-terminating reverse proxy, written in anger on top of pingora
at main 255 lines 8.2 kB view raw
1syntax = "proto3"; 2 3package config.format; 4 5// the root config 6// 7// each of these fields will show up at the root of your config file 8// 9// at the minimum, you'll need at least 1 `domains` entry to define your 10// sites, and one `bind_to_tcp` entry to define how the proxy itself 11// serves content. 12message Config { 13 // the domains to serve 14 map<string, Domain> domains = 1; 15 16 // bind to tcp ports, with optional tls 17 repeated TCPBinding bind_to_tcp = 2; 18 19 // lower-level pingora config 20 // 21 // (you probably don't need to touch this) 22 Pingora pingora = 3; 23} 24 25// a single, served domain 26// 27// you'll want at least 1 backend (`https`, `http`, or `uds`), 28// but you can repeat each one to define multiple backends 29// and load-balance between them, and you can (if you really need 30// to) mix and match types. 31// 32// you may also want an (optional) `oidc_auth` configuration to turn on 33// authentication-termination 34// 35// you can also use `manage_headers` to inject things like `X-Forwarded-For`, 36// or clear headers that you don't want going to your backend. 37message Domain { 38 // require oidc auth if this is set 39 optional OIDC oidc_auth = 1; 40 41 // https backends 42 repeated HTTPSBackend https = 3; 43 // http backends 44 repeated HTTPSBackend http = 7; 45 // unix domain socket backends 46 repeated UDSBackend uds = 4; 47 48 enum TLSMode { 49 // don't support redirects, they can be _very_ unsafe. just use hsts 50 51 // only allow https, no redirect 52 TLS_MODE_ONLY = 0; 53 // allow http, for testing purposes 54 TLS_MODE_UNSAFE_ALLOW_HTTP = 1; 55 } 56 57 // allow disabling TLS termination, for testing 58 TLSMode tls_mode = 5; 59 60 // set or clear headers on the backend request 61 ManageHeaders manage_headers = 6; 62 63 // configure tls settings 64 message TLS { 65 // path to the (public) tls certificate, with all intermediate certificates (fullchain.pem for most acme clients) 66 string cert_path = 1; 67 // path to the (privat) tls key 68 string key_path = 2; 69 // override the default (using the domain name) for SNI purposes 70 // useful if the domain contains a port 71 optional string sni = 3; 72 } 73 74 // configure tls 75 // 76 // you should _always_ have this 77 TLS tls = 8; 78} 79 80// configure oidc/oauth v2.1 termination 81message OIDC { 82 // the base oidc discovery url, without the `.well-known/openid-configuration` part 83 // 84 // per [OIDC Discovery 1.0](https://openid.net/specs/openid-connect-discovery-1_0.html) 85 // and [RFC 8414](https://datatracker.ietf.org/doc/html/rfc8414). 86 string discovery_url_base = 1; 87 88 // your oauth client id, from your oauth provider 89 string client_id = 2; 90 // your oauth client secret, from your oauth provider 91 string client_secret_path = 3; 92 93 // a set of scopes you wish to ask the sever for 94 // 95 // (`openid` will always be automatically included) 96 Scopes scopes = 4; 97 // map returned "claims" (pieces of user information) 98 // to header in the backend request. 99 // 100 // for example, you can use this to tell backend services the name of the 101 // authenticated user. 102 Claims claims = 5; 103 104 // per oidc core v1-with-errata-2§3.1.3.7 point 6, we _may_ skip validation 105 // of the id token if it was received over tls. which it will be, in our 106 // case. some folks may want to be extra paranoid, but generally you either 107 // trust tls, or you can't trust discovery, and thus can't trust the jwks info, 108 // so default this to false. 109 // 110 // generally, you can leave this off. 111 bool validate_with_jwk = 6; 112 113 // where to redirect to on logout 114 string logout_url = 7; 115} 116 117// scopes to ask the server for 118message Scopes { 119 // the scopes you need from the server for your claims map to work, or that 120 // you want to request to ensure that the user is authorized to continue to 121 // your site. 122 repeated string required = 2; 123 124 // optional scopes, not requested from the server 125 // 126 // not currently used 127 repeated string optional = 1; 128} 129 130// information on how to process returned claims 131message Claims { 132 // maps a claim to a header name & value 133 message ClaimToHeader { 134 // the name of the claim 135 string claim = 1; 136 // the name to the header 137 string header = 2; 138 // how to serialize compound (object, array) typed-claims 139 optional Serialization serialize_as = 3; 140 } 141 142 // map the given claims to a header in the backend request 143 // 144 // headers specified here with no corresponding claim value will be wiped 145 repeated ClaimToHeader claim_to_header = 1; 146 147 // how to serialize compound (object, array) typed-claims 148 // 149 // this doesn't cover _deeply_ nested identical objects (e.g. 150 // arrays-of-arrays), or selecting/projecting, but covers most usecases. in 151 // theory i could implement, say, jq or something but that feels like 152 // overkill. 153 // 154 // values that are `null` will always be skipped, and map entries with a null 155 // value with also be skipped. 156 message Serialization { 157 // how to join keys to values (e.g. `=` means `key=value`) 158 optional string join_keys_and_values_with = 1; 159 // how to join key-value pairs to each other (e.g. `; ` means `key=value; key=value`) 160 optional string join_key_value_pairs_with = 2; 161 // how to join array items to each other (e.g. `, ` means `item, item, item`) 162 optional string join_array_items_with = 3; 163 } 164} 165 166// a standard https backend 167message HTTPSBackend { 168 // full ipv4 or v6 address, including port 169 string addr = 1; 170 // weight of this backend, if load-balancing 171 optional uint64 weight = 2; 172 173 // skip verifying certificates on tls backends 174 // 175 // useful if your backend certs are self-signed, 176 // or for some reason not valid for the backend itself. 177 // 178 // generally prefer to use `ca_path` instead. 179 bool skip_verifying_certs = 3; 180 181 // path to the CA file used to verify the backend's certs, on tls backends 182 // 183 // useful if the backend is using self-signed certs 184 optional string ca_path = 4; 185} 186 187// a unix domain socket backend 188message UDSBackend { 189 // path to the uds socket 190 string path = 1; 191 // weight of this backend, if load-balancing 192 optional uint64 weight = 2; 193} 194 195// these headers will be set if they are set to something 196// 197// either way, they will be wiped if the client tries to send them 198message ManageHeaders { 199 // set the given header to be the request host 200 optional string host = 1; 201 // set an `X-Forwarded-For`-style header, appending `,<remote_addr>` to any existing value 202 optional string x_forwarded_for = 2; 203 // set an `X-Forwarded-Proto`-style header to the original scheme of the request 204 optional string x_forwarded_proto = 3; 205 // set an `X-Real-IP`-style header (i.e. _just_ the remote address) 206 optional string remote_addr = 4; 207 208 // always clear these headers 209 repeated string always_clear = 5; 210} 211 212// equivalent to [`pingora::server::configuration::Config`] 213// 214// See [pingora](https://docs.rs/pingora/latest/pingora/server/configuration/struct.ServerConf.html) 215// for details on what these do 216message Pingora { 217 optional uint64 version = 1; 218 optional bool daemon = 2; 219 optional string error_log = 3; 220 optional string pid_file = 4; 221 optional string upgrade_sock = 5; 222 optional string user = 6; 223 optional string group = 7; 224 optional uint64 threads = 8; 225 optional uint64 listener_tasks_per_fd = 9; 226 optional bool work_stealing = 10; 227 optional string ca_file = 11; 228 optional uint64 grace_period_seconds = 12; 229 optional uint64 graceful_shutdown_timeout_seconds = 13; 230 repeated string client_bind_to_ipv4 = 14; 231 repeated string client_bind_to_ipv6 = 15; 232 optional uint64 upstream_keepalive_pool_size = 16; 233 optional uint64 upstream_connect_offload_threadpools = 17; 234 optional uint64 upstream_connect_offload_thread_per_pool = 18; 235 optional bool upstream_debug_ssl_keylog = 19; 236 optional uint64 max_retries = 20; 237 optional uint64 upgrade_sock_connect_accept_max_retries = 21; 238 239} 240 241// bind to a tcp port 242// 243// this configures which ports you'll serve https & grpc traffic on. 244// 245// you'll want to chose an address. 246// 247// tls is configured per-domain in the domain settings. 248message TCPBinding { 249 // TODO(feature): default tls settings 250 251 // host an port to bind to 252 string addr = 1; 253 254 // TODO(feature): surface tcp options from pingora 255}