lds.template.yaml994 lines · main
1resources:
2 - '@type': type.googleapis.com/envoy.config.listener.v3.Listener
3 name: briven
4 per_connection_buffer_limit_bytes: 32768 # 32 KiB
5
6 address:
7 socket_address:
8 address: 0.0.0.0
9 port_value: 8000
10
11 filter_chains:
12 - filters:
13 - name: envoy.filters.network.http_connection_manager
14 typed_config:
15 '@type': >-
16 type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
17 stat_prefix: ingress_http
18 normalize_path: true
19 merge_slashes: true
20 path_with_escaped_slashes_action: REJECT_REQUEST
21 use_remote_address: true
22 common_http_protocol_options:
23 headers_with_underscores_action: REJECT_REQUEST
24 upgrade_configs:
25 - upgrade_type: websocket
26 access_log:
27 - name: envoy.access_loggers.stdout
28 typed_config:
29 '@type': >-
30 type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog
31 log_format:
32 text_format_source:
33 inline_string: "%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT% - - [%START_TIME(%d/%b/%Y:%H:%M:%S %z)%] \"%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%\" %RESPONSE_CODE% %BYTES_SENT% \"%REQ(REFERER)%\" \"%REQ(USER-AGENT)%\"\n"
34
35 route_config:
36 name: briven_route
37 virtual_hosts:
38 - name: briven_host
39 domains:
40 - '*'
41 cors:
42 allow_origin_string_match:
43 - safe_regex:
44 regex: ".*"
45 allow_methods: "GET,POST,PUT,PATCH,DELETE,OPTIONS,HEAD,CONNECT,TRACE"
46 allow_headers: "*"
47 expose_headers: "*"
48 max_age: "3600"
49 request_headers_to_add:
50 - header:
51 key: X-Forwarded-Host
52 value: "%REQ(:AUTHORITY)%"
53 append_action: ADD_IF_ABSENT
54 - header:
55 key: X-Forwarded-Port
56 value: "%DOWNSTREAM_LOCAL_PORT%"
57 append_action: ADD_IF_ABSENT
58 routes:
59 - match:
60 prefix: /auth/v1/verify
61 route:
62 cluster: auth
63 prefix_rewrite: /verify
64 timeout: 30s
65 request_headers_to_add:
66 - header:
67 key: X-Forwarded-Prefix
68 value: /auth/v1/verify
69 append_action: ADD_IF_ABSENT
70 typed_per_filter_config:
71 envoy.filters.http.basic_auth:
72 '@type': >-
73 type.googleapis.com/envoy.config.route.v3.FilterConfig
74 disabled: true
75 envoy.filters.http.rbac:
76 '@type': >-
77 type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBACPerRoute
78 rbac:
79 rules:
80 action: ALLOW
81 policies:
82 allow_all:
83 permissions:
84 - any: true
85 principals:
86 - any: true
87
88 - match:
89 prefix: /auth/v1/callback
90 route:
91 cluster: auth
92 prefix_rewrite: /callback
93 timeout: 30s
94 request_headers_to_add:
95 - header:
96 key: X-Forwarded-Prefix
97 value: /auth/v1/callback
98 append_action: ADD_IF_ABSENT
99 typed_per_filter_config:
100 envoy.filters.http.basic_auth:
101 '@type': >-
102 type.googleapis.com/envoy.config.route.v3.FilterConfig
103 disabled: true
104 envoy.filters.http.rbac:
105 '@type': >-
106 type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBACPerRoute
107 rbac:
108 rules:
109 action: ALLOW
110 policies:
111 allow_all:
112 permissions:
113 - any: true
114 principals:
115 - any: true
116
117 - match:
118 prefix: /auth/v1/authorize
119 route:
120 cluster: auth
121 prefix_rewrite: /authorize
122 timeout: 30s
123 request_headers_to_add:
124 - header:
125 key: X-Forwarded-Prefix
126 value: /auth/v1/authorize
127 append_action: ADD_IF_ABSENT
128 typed_per_filter_config:
129 envoy.filters.http.basic_auth:
130 '@type': >-
131 type.googleapis.com/envoy.config.route.v3.FilterConfig
132 disabled: true
133 envoy.filters.http.rbac:
134 '@type': >-
135 type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBACPerRoute
136 rbac:
137 rules:
138 action: ALLOW
139 policies:
140 allow_all:
141 permissions:
142 - any: true
143 principals:
144 - any: true
145
146 - match:
147 prefix: /auth/v1/.well-known/jwks.json
148 route:
149 cluster: auth
150 prefix_rewrite: /.well-known/jwks.json
151 timeout: 30s
152 request_headers_to_add:
153 - header:
154 key: X-Forwarded-Prefix
155 value: /auth/v1/.well-known/jwks.json
156 append_action: ADD_IF_ABSENT
157 typed_per_filter_config:
158 envoy.filters.http.basic_auth:
159 '@type': >-
160 type.googleapis.com/envoy.config.route.v3.FilterConfig
161 disabled: true
162 envoy.filters.http.rbac:
163 '@type': >-
164 type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBACPerRoute
165 rbac:
166 rules:
167 action: ALLOW
168 policies:
169 allow_all:
170 permissions:
171 - any: true
172 principals:
173 - any: true
174
175 - match:
176 prefix: /.well-known/oauth-authorization-server
177 route:
178 cluster: auth
179 timeout: 30s
180 request_headers_to_add:
181 - header:
182 key: X-Forwarded-Prefix
183 value: /.well-known/oauth-authorization-server
184 append_action: ADD_IF_ABSENT
185 typed_per_filter_config:
186 envoy.filters.http.basic_auth:
187 '@type': >-
188 type.googleapis.com/envoy.config.route.v3.FilterConfig
189 disabled: true
190 envoy.filters.http.rbac:
191 '@type': >-
192 type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBACPerRoute
193 rbac:
194 rules:
195 action: ALLOW
196 policies:
197 allow_all:
198 permissions:
199 - any: true
200 principals:
201 - any: true
202
203 - match:
204 prefix: /sso/saml/acs
205 route:
206 cluster: auth
207 timeout: 30s
208 request_headers_to_add:
209 - header:
210 key: X-Forwarded-Prefix
211 value: /sso/saml/acs
212 append_action: ADD_IF_ABSENT
213 typed_per_filter_config:
214 envoy.filters.http.basic_auth:
215 '@type': >-
216 type.googleapis.com/envoy.config.route.v3.FilterConfig
217 disabled: true
218 envoy.filters.http.rbac:
219 '@type': >-
220 type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBACPerRoute
221 rbac:
222 rules:
223 action: ALLOW
224 policies:
225 allow_all:
226 permissions:
227 - any: true
228 principals:
229 - any: true
230
231 - match:
232 prefix: /sso/saml/metadata
233 route:
234 cluster: auth
235 timeout: 30s
236 request_headers_to_add:
237 - header:
238 key: X-Forwarded-Prefix
239 value: /sso/saml/metadata
240 append_action: ADD_IF_ABSENT
241 typed_per_filter_config:
242 envoy.filters.http.basic_auth:
243 '@type': >-
244 type.googleapis.com/envoy.config.route.v3.FilterConfig
245 disabled: true
246 envoy.filters.http.rbac:
247 '@type': >-
248 type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBACPerRoute
249 rbac:
250 rules:
251 action: ALLOW
252 policies:
253 allow_all:
254 permissions:
255 - any: true
256 principals:
257 - any: true
258
259 - name: functions-v1-all
260 match:
261 prefix: /functions/v1/
262 route:
263 cluster: functions
264 prefix_rewrite: /
265 timeout: 150s
266 request_headers_to_add:
267 - header:
268 key: X-Forwarded-Prefix
269 value: /functions/v1/
270 append_action: ADD_IF_ABSENT
271 typed_per_filter_config:
272 envoy.filters.http.basic_auth:
273 '@type': >-
274 type.googleapis.com/envoy.config.route.v3.FilterConfig
275 disabled: true
276 envoy.filters.http.rbac:
277 '@type': >-
278 type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBACPerRoute
279 rbac:
280 rules:
281 action: ALLOW
282 policies:
283 allow_all:
284 permissions:
285 - any: true
286 principals:
287 - any: true
288
289 - match:
290 prefix: /storage/v1/
291 route:
292 cluster: storage
293 prefix_rewrite: /
294 timeout: 30s
295 request_headers_to_add:
296 - header:
297 key: X-Forwarded-Prefix
298 value: /storage/v1
299 append_action: ADD_IF_ABSENT
300 typed_per_filter_config:
301 envoy.filters.http.basic_auth:
302 '@type': >-
303 type.googleapis.com/envoy.config.route.v3.FilterConfig
304 disabled: true
305 envoy.filters.http.rbac:
306 '@type': >-
307 type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBACPerRoute
308 rbac:
309 rules:
310 action: ALLOW
311 policies:
312 allow_all:
313 permissions:
314 - any: true
315 principals:
316 - any: true
317
318 - name: auth-v1-protected
319 match:
320 prefix: /auth/v1/
321 route:
322 cluster: auth
323 prefix_rewrite: /
324 timeout: 30s
325 request_headers_to_add:
326 - header:
327 key: X-Forwarded-Prefix
328 value: /auth/v1/
329 append_action: ADD_IF_ABSENT
330 typed_per_filter_config:
331 envoy.filters.http.basic_auth:
332 '@type': >-
333 type.googleapis.com/envoy.config.route.v3.FilterConfig
334 disabled: true
335
336 - name: rest-v1-protected
337 match:
338 prefix: /rest/v1/
339 route:
340 cluster: rest
341 prefix_rewrite: /
342 timeout: 30s
343 request_headers_to_add:
344 - header:
345 key: X-Forwarded-Prefix
346 value: /rest/v1/
347 append_action: ADD_IF_ABSENT
348 typed_per_filter_config:
349 envoy.filters.http.basic_auth:
350 '@type': >-
351 type.googleapis.com/envoy.config.route.v3.FilterConfig
352 disabled: true
353
354 - name: graphql-v1-protected
355 match:
356 prefix: /graphql/v1
357 route:
358 cluster: rest
359 prefix_rewrite: /rpc/graphql
360 timeout: 30s
361 request_headers_to_add:
362 - header:
363 key: X-Forwarded-Prefix
364 value: /graphql/v1
365 append_action: ADD_IF_ABSENT
366 - header:
367 key: Content-Profile
368 value: graphql_public
369 append_action: ADD_IF_ABSENT
370 typed_per_filter_config:
371 envoy.filters.http.basic_auth:
372 '@type': >-
373 type.googleapis.com/envoy.config.route.v3.FilterConfig
374 disabled: true
375
376 - name: realtime-v1-api-protected
377 match:
378 prefix: /realtime/v1/api
379 route:
380 cluster: realtime
381 prefix_rewrite: /api
382 timeout: 30s
383 host_rewrite_literal: realtime-dev.briven-realtime
384 request_headers_to_add:
385 - header:
386 key: X-Forwarded-Prefix
387 value: /realtime/v1/api
388 append_action: ADD_IF_ABSENT
389 typed_per_filter_config:
390 envoy.filters.http.basic_auth:
391 '@type': >-
392 type.googleapis.com/envoy.config.route.v3.FilterConfig
393 disabled: true
394
395 - name: realtime-v1-ws-protected
396 match:
397 prefix: /realtime/v1/
398 route:
399 cluster: realtime
400 prefix_rewrite: /socket/
401 timeout: 30s
402 host_rewrite_literal: realtime-dev.briven-realtime
403 request_headers_to_add:
404 - header:
405 key: X-Forwarded-Prefix
406 value: /realtime/v1/
407 append_action: ADD_IF_ABSENT
408 typed_per_filter_config:
409 envoy.filters.http.basic_auth:
410 '@type': >-
411 type.googleapis.com/envoy.config.route.v3.FilterConfig
412 disabled: true
413
414 - name: pg-protected
415 match:
416 prefix: /pg/
417 route:
418 cluster: meta
419 prefix_rewrite: /
420 timeout: 30s
421 request_headers_to_add:
422 - header:
423 key: X-Forwarded-Prefix
424 value: /pg/
425 append_action: ADD_IF_ABSENT
426 typed_per_filter_config:
427 envoy.filters.http.basic_auth:
428 '@type': >-
429 type.googleapis.com/envoy.config.route.v3.FilterConfig
430 disabled: true
431
432 - match:
433 prefix: /api/mcp
434 route:
435 cluster: studio
436 timeout: 30s
437 request_headers_to_add:
438 - header:
439 key: X-Forwarded-Prefix
440 value: /api/mcp
441 append_action: ADD_IF_ABSENT
442 typed_per_filter_config:
443 envoy.filters.http.basic_auth:
444 '@type': >-
445 type.googleapis.com/envoy.config.route.v3.FilterConfig
446 disabled: true
447 envoy.filters.http.rbac:
448 '@type': >-
449 type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBACPerRoute
450 rbac:
451 rules:
452 action: DENY
453 policies:
454 deny_all:
455 permissions:
456 - any: true
457 principals:
458 - any: true
459
460 - match:
461 prefix: /mcp
462 route:
463 cluster: studio
464 prefix_rewrite: /api/mcp
465 timeout: 30s
466 request_headers_to_add:
467 - header:
468 key: X-Forwarded-Prefix
469 value: /mcp
470 append_action: ADD_IF_ABSENT
471 typed_per_filter_config:
472 envoy.filters.http.basic_auth:
473 '@type': >-
474 type.googleapis.com/envoy.config.route.v3.FilterConfig
475 disabled: true
476 envoy.filters.http.rbac:
477 '@type': >-
478 type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBACPerRoute
479 # Block access to /mcp by default
480 rbac:
481 rules:
482 action: DENY
483 policies:
484 deny_all:
485 permissions:
486 - any: true
487 principals:
488 - any: true
489 # Enable local access (danger zone!)
490 # 1. Comment out the 'rbac' block above.
491 # 2. Uncomment and adjust the 'rbac' block below.
492 # 3. Add or adjust your local IPs in 'principals'.
493 #rbac:
494 # rules:
495 # action: ALLOW
496 # policies:
497 # allow_local:
498 # permissions:
499 # - any: true
500 # principals:
501 # - direct_remote_ip:
502 # address_prefix: 127.0.0.1
503 # prefix_len: 32
504 # - direct_remote_ip:
505 # address_prefix: ::1
506 # prefix_len: 128
507
508 - match:
509 prefix: /
510 route:
511 cluster: studio
512 timeout: 30s
513 request_headers_to_remove:
514 - authorization
515 request_headers_to_add:
516 - header:
517 key: X-Forwarded-Prefix
518 value: /
519 append_action: ADD_IF_ABSENT
520 typed_per_filter_config:
521 envoy.filters.http.rbac:
522 '@type': >-
523 type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBACPerRoute
524 rbac:
525 rules:
526 action: ALLOW
527 policies:
528 allow_all:
529 permissions:
530 - any: true
531 principals:
532 - any: true
533
534 http_filters:
535 - name: envoy.filters.http.cors
536 typed_config:
537 '@type': >-
538 type.googleapis.com/envoy.extensions.filters.http.cors.v3.Cors
539
540 - name: envoy.filters.http.basic_auth
541 typed_config:
542 '@type': >-
543 type.googleapis.com/envoy.extensions.filters.http.basic_auth.v3.BasicAuth
544 users:
545 inline_string: '${DASHBOARD_BASIC_AUTH}'
546
547 # Copies ?apikey=... from the URL into the apikey header when clients omit the header.
548 - name: envoy.filters.http.lua
549 typed_config:
550 '@type': >-
551 type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
552 inline_code: |
553 local FUNCTIONS_ROUTE = "functions-v1-all"
554 local FUNCTIONS_PREFIX = "/functions/v1/"
555
556 local function is_functions_request(request_handle, headers)
557 if request_handle:streamInfo():routeName() == FUNCTIONS_ROUTE then
558 return true
559 end
560
561 local path = headers:get(":path")
562 if path == nil then
563 return false
564 end
565
566 return string.sub(path, 1, string.len(FUNCTIONS_PREFIX)) == FUNCTIONS_PREFIX
567 end
568
569 function envoy_on_request(request_handle)
570 local headers = request_handle:headers()
571 if is_functions_request(request_handle, headers) then
572 return
573 end
574
575 if headers:get("apikey") ~= nil then
576 return
577 end
578
579 local path = headers:get(":path")
580 local query_start = string.find(path, "?", 1, true)
581 if query_start == nil then
582 return
583 end
584
585 local query = string.sub(path, query_start + 1)
586 for key, value in string.gmatch(query, "([^&]+)=([^&]*)") do
587 if key == "apikey" and value ~= "" then
588 headers:add("apikey", value)
589 return
590 end
591 end
592 end
593
594 # Translates the query parameter apikey into the matching internal JWT and rewrites the URL so only JWTs propagate downstream.
595 - name: envoy.filters.http.lua
596 typed_config:
597 '@type': >-
598 type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
599 inline_code: |
600 local FUNCTIONS_ROUTE = "functions-v1-all"
601 local FUNCTIONS_PREFIX = "/functions/v1/"
602 local SECRET_KEY = "${BRIVEN_SECRET_KEY}"
603 local PUBLISHABLE_KEY = "${BRIVEN_PUBLISHABLE_KEY}"
604 local SERVICE_ROLE_JWT = "${SERVICE_ROLE_KEY_ASYMMETRIC}"
605 local ANON_JWT = "${ANON_KEY_ASYMMETRIC}"
606 local TRANSLATION_ENABLED = SECRET_KEY ~= "" and PUBLISHABLE_KEY ~= "" and SERVICE_ROLE_JWT ~= "" and ANON_JWT ~= ""
607
608 local function is_functions_request(request_handle, headers)
609 if request_handle:streamInfo():routeName() == FUNCTIONS_ROUTE then
610 return true
611 end
612
613 local path = headers:get(":path")
614 if path == nil then
615 return false
616 end
617
618 return string.sub(path, 1, string.len(FUNCTIONS_PREFIX)) == FUNCTIONS_PREFIX
619 end
620
621 local function translate_apikey(apikey)
622 if apikey == nil or apikey == "" then
623 return nil
624 end
625
626 if not TRANSLATION_ENABLED then
627 return nil
628 end
629
630 if apikey == SECRET_KEY then
631 return SERVICE_ROLE_JWT
632 end
633
634 if apikey == PUBLISHABLE_KEY then
635 return ANON_JWT
636 end
637
638 return nil
639 end
640
641 local function extract_query_apikey(path)
642 if path == nil or path == "" then
643 return nil
644 end
645
646 local query_start = string.find(path, "?", 1, true)
647 if query_start == nil then
648 return nil
649 end
650
651 local query = string.sub(path, query_start + 1)
652 for key, value in string.gmatch(query, "([^&]+)=([^&]*)") do
653 if key == "apikey" and value ~= "" then
654 return value
655 end
656 end
657
658 return nil
659 end
660
661 local function replace_query_apikey(path, new_value)
662 if path == nil or path == "" or new_value == nil or new_value == "" then
663 return nil
664 end
665
666 local query_start = string.find(path, "?", 1, true)
667 if query_start == nil then
668 return nil
669 end
670
671 local base = string.sub(path, 1, query_start)
672 local query = string.sub(path, query_start + 1)
673 local updated = {}
674 local replaced = false
675
676 for part in string.gmatch(query, "([^&]+)") do
677 local key, value = string.match(part, "([^=]+)=(.*)")
678 if key == "apikey" then
679 part = key .. "=" .. new_value
680 replaced = true
681 end
682 table.insert(updated, part)
683 end
684
685 if not replaced then
686 return nil
687 end
688
689 return base .. table.concat(updated, "&")
690 end
691
692 function envoy_on_request(request_handle)
693 local headers = request_handle:headers()
694 if is_functions_request(request_handle, headers) then
695 return
696 end
697
698 local path = headers:get(":path")
699 local apikey = extract_query_apikey(path)
700 local translated = translate_apikey(apikey)
701
702 if translated == nil then
703 return
704 end
705
706 headers:replace("apikey", translated)
707
708 local rewritten_path = replace_query_apikey(path, translated)
709 if rewritten_path ~= nil then
710 headers:replace(":path", rewritten_path)
711 end
712 end
713
714 # Translates an apikey header into the appropriate internal JWT for downstream RBAC checks.
715 - name: envoy.filters.http.lua
716 typed_config:
717 '@type': >-
718 type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
719 inline_code: |
720 local FUNCTIONS_ROUTE = "functions-v1-all"
721 local FUNCTIONS_PREFIX = "/functions/v1/"
722 local SECRET_KEY = "${BRIVEN_SECRET_KEY}"
723 local PUBLISHABLE_KEY = "${BRIVEN_PUBLISHABLE_KEY}"
724 local SERVICE_ROLE_JWT = "${SERVICE_ROLE_KEY_ASYMMETRIC}"
725 local ANON_JWT = "${ANON_KEY_ASYMMETRIC}"
726 local TRANSLATION_ENABLED = SECRET_KEY ~= "" and PUBLISHABLE_KEY ~= "" and SERVICE_ROLE_JWT ~= "" and ANON_JWT ~= ""
727
728 local function is_functions_request(request_handle, headers)
729 if request_handle:streamInfo():routeName() == FUNCTIONS_ROUTE then
730 return true
731 end
732
733 local path = headers:get(":path")
734 if path == nil then
735 return false
736 end
737
738 return string.sub(path, 1, string.len(FUNCTIONS_PREFIX)) == FUNCTIONS_PREFIX
739 end
740
741 local function translate_apikey(apikey)
742 if apikey == nil or apikey == "" then
743 return nil
744 end
745
746 if not TRANSLATION_ENABLED then
747 return nil
748 end
749
750 if apikey == SECRET_KEY then
751 return SERVICE_ROLE_JWT
752 end
753
754 if apikey == PUBLISHABLE_KEY then
755 return ANON_JWT
756 end
757
758 return nil
759 end
760
761 function envoy_on_request(request_handle)
762 local headers = request_handle:headers()
763 if is_functions_request(request_handle, headers) then
764 return
765 end
766
767 local translated = translate_apikey(headers:get("apikey"))
768 if translated ~= nil and translated ~= "" then
769 headers:replace("apikey", translated)
770 end
771 end
772
773 # Mirrors apikey into x-api-key for realtime WS compatibility.
774 - name: envoy.filters.http.lua
775 typed_config:
776 '@type': >-
777 type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
778 inline_code: |
779 local REALTIME_WS_ROUTE = "realtime-v1-ws-protected"
780
781 function envoy_on_request(request_handle)
782 local route_name = request_handle:streamInfo():routeName()
783 if route_name ~= REALTIME_WS_ROUTE then
784 return
785 end
786
787 local headers = request_handle:headers()
788 local apikey = headers:get("apikey")
789 if apikey == nil or apikey == "" then
790 return
791 end
792
793 headers:replace("x-api-key", apikey)
794 end
795
796 # Synthesizes an Authorization header (Bearer …) from apikey when callers don’t provide a real JWT header.
797 - name: envoy.filters.http.lua
798 typed_config:
799 '@type': >-
800 type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
801 inline_code: |
802 local FUNCTIONS_ROUTE = "functions-v1-all"
803 local FUNCTIONS_PREFIX = "/functions/v1/"
804 local REALTIME_WS_ROUTE = "realtime-v1-ws-protected"
805
806 local function is_functions_request(request_handle, headers)
807 if request_handle:streamInfo():routeName() == FUNCTIONS_ROUTE then
808 return true
809 end
810
811 local path = headers:get(":path")
812 if path == nil then
813 return false
814 end
815
816 return string.sub(path, 1, string.len(FUNCTIONS_PREFIX)) == FUNCTIONS_PREFIX
817 end
818
819 local function has_real_jwt(auth_header)
820 if auth_header == nil or auth_header == "" then
821 return false
822 end
823
824 if string.sub(auth_header, 1, 7) ~= "Bearer " then
825 return false
826 end
827
828 return string.sub(auth_header, 1, 10) ~= "Bearer sb_"
829 end
830
831 local function format_authorization(value)
832 if value == nil or value == "" then
833 return nil
834 end
835
836 if string.sub(value, 1, 7) == "Bearer " then
837 return value
838 end
839
840 return "Bearer " .. value
841 end
842
843 function envoy_on_request(request_handle)
844 local headers = request_handle:headers()
845 if request_handle:streamInfo():routeName() == REALTIME_WS_ROUTE then
846 return
847 end
848
849 if is_functions_request(request_handle, headers) then
850 return
851 end
852
853 if has_real_jwt(headers:get("authorization")) then
854 return
855 end
856
857 local apikey = headers:get("apikey")
858 local authorization_value = format_authorization(apikey)
859 if authorization_value ~= nil then
860 headers:replace("authorization", authorization_value)
861 end
862 end
863
864 # Returns 401 for missing/invalid API keys on protected API routes.
865 - name: envoy.filters.http.lua
866 typed_config:
867 '@type': >-
868 type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
869 inline_code: |
870 local ANON_KEY = "${ANON_KEY}"
871 local SERVICE_ROLE_KEY = "${SERVICE_ROLE_KEY}"
872 local BRIVEN_PUBLISHABLE_KEY = "${BRIVEN_PUBLISHABLE_KEY}"
873 local BRIVEN_SECRET_KEY = "${BRIVEN_SECRET_KEY}"
874 local ANON_KEY_ASYMMETRIC = "${ANON_KEY_ASYMMETRIC}"
875 local SERVICE_ROLE_KEY_ASYMMETRIC = "${SERVICE_ROLE_KEY_ASYMMETRIC}"
876 local TRANSLATION_ENABLED = BRIVEN_SECRET_KEY ~= "" and BRIVEN_PUBLISHABLE_KEY ~= "" and SERVICE_ROLE_KEY_ASYMMETRIC ~= "" and ANON_KEY_ASYMMETRIC ~= ""
877
878 local PROTECTED_ROUTES = {
879 ["auth-v1-protected"] = true,
880 ["rest-v1-protected"] = true,
881 ["graphql-v1-protected"] = true,
882 ["realtime-v1-api-protected"] = true,
883 ["realtime-v1-ws-protected"] = true,
884 ["pg-protected"] = true,
885 }
886
887 local function is_protected_route(route_name)
888 if route_name == nil or route_name == "" then
889 return false
890 end
891
892 return PROTECTED_ROUTES[route_name] == true
893 end
894
895 local function is_valid_apikey(apikey)
896 if apikey == nil or apikey == "" then
897 return false
898 end
899
900 if SERVICE_ROLE_KEY ~= "" and apikey == SERVICE_ROLE_KEY then
901 return true
902 end
903
904 if ANON_KEY ~= "" and apikey == ANON_KEY then
905 return true
906 end
907
908 if TRANSLATION_ENABLED and apikey == SERVICE_ROLE_KEY_ASYMMETRIC then
909 return true
910 end
911
912 if TRANSLATION_ENABLED and apikey == ANON_KEY_ASYMMETRIC then
913 return true
914 end
915
916 return false
917 end
918
919 function envoy_on_request(request_handle)
920 local headers = request_handle:headers()
921 local route_name = request_handle:streamInfo():routeName()
922 if not is_protected_route(route_name) then
923 return
924 end
925
926 if is_valid_apikey(headers:get("apikey")) then
927 return
928 end
929
930 request_handle:respond({
931 [":status"] = "401",
932 ["content-type"] = "text/plain",
933 }, "Unauthorized")
934 end
935
936 - name: envoy.filters.http.rbac
937 typed_config:
938 '@type': >-
939 type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC
940 rules:
941 action: ALLOW
942 policies:
943 admin:
944 permissions:
945 - url_path:
946 path:
947 prefix: /pg/
948 principals:
949 - header:
950 name: apikey
951 string_match:
952 exact: '${SERVICE_ROLE_KEY}'
953 - header:
954 name: apikey
955 string_match:
956 exact: '${SERVICE_ROLE_KEY_ASYMMETRIC}'
957 apikey:
958 permissions:
959 - url_path:
960 path:
961 prefix: /auth/v1/
962 - url_path:
963 path:
964 prefix: /rest/v1/
965 - url_path:
966 path:
967 prefix: /realtime/v1/api
968 - url_path:
969 path:
970 prefix: /realtime/v1/
971 - url_path:
972 path:
973 prefix: /graphql/v1
974 principals:
975 - header:
976 name: apikey
977 string_match:
978 exact: '${SERVICE_ROLE_KEY}'
979 - header:
980 name: apikey
981 string_match:
982 exact: '${ANON_KEY}'
983 - header:
984 name: apikey
985 string_match:
986 exact: '${SERVICE_ROLE_KEY_ASYMMETRIC}'
987 - header:
988 name: apikey
989 string_match:
990 exact: '${ANON_KEY_ASYMMETRIC}'
991 - name: envoy.filters.http.router
992 typed_config:
993 '@type': >-
994 type.googleapis.com/envoy.extensions.filters.http.router.v3.Router