supabase-postgres.git / summary / log / commit / refs
commit dd307fb2cad5d6101331f6d0b2bfc3389287a5f1
Author: Stojan Dimitrovski <sdimitrovski@gmail.com>
Commit: GitHub <noreply@github.com>
Date: Wed Nov 20 13:49:06 2024 +0000
feat: separate envoy lds configs for self-hosting and supabase use-cases (#1325)
* feat: separate envoy lds configs for self-hosting and supabase use-cases
* feat: add origin protection key enforcement for envoy in `lds.supabase.yaml`
.github/workflows/dockerhub-release-aio.yml | 1 +
ansible/files/envoy_config/lds.supabase.yaml | 436 +++++++++++++++++++++++++++
ansible/tasks/setup-supabase-internal.yml | 3 +
ansible/vars.yml | 4 +-
docker/all-in-one/Dockerfile | 3 +
5 files changed, 445 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/dockerhub-release-aio.yml b/.github/workflows/dockerhub-release-aio.yml
index e5a5a825..ce0efb59 100644
--- a/.github/workflows/dockerhub-release-aio.yml
+++ b/.github/workflows/dockerhub-release-aio.yml
@@ -74,6 +74,7 @@ jobs:
push: true
build-args: |
postgres_version=${{ needs.settings.outputs.base_docker_version }}
+ envoy_lds=lds.supabase.yaml
${{ needs.settings.outputs.build_args }}
target: production
tags: ${{ needs.settings.outputs.image_tag }}_${{ matrix.arch }}
diff --git a/ansible/files/envoy_config/lds.supabase.yaml b/ansible/files/envoy_config/lds.supabase.yaml
new file mode 100644
index 00000000..2fc7cae1
--- /dev/null
+++ b/ansible/files/envoy_config/lds.supabase.yaml
@@ -0,0 +1,436 @@
+resources:
+ - '@type': type.googleapis.com/envoy.config.listener.v3.Listener
+ name: http_listener
+ address:
+ socket_address:
+ address: '::'
+ port_value: 80
+ ipv4_compat: true
+ filter_chains:
+ - filters: &ref_1
+ - name: envoy.filters.network.http_connection_manager
+ typed_config:
+ '@type': >-
+ type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
+ access_log:
+ - name: envoy.access_loggers.stdout
+ filter:
+ status_code_filter:
+ comparison:
+ op: GE
+ value:
+ default_value: 400
+ runtime_key: unused
+ typed_config:
+ '@type': >-
+ type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog
+ generate_request_id: false
+ http_filters:
+ - name: envoy.filters.http.cors
+ typed_config:
+ '@type': >-
+ type.googleapis.com/envoy.extensions.filters.http.cors.v3.Cors
+ - name: envoy.filters.http.rbac
+ typed_config:
+ '@type': >-
+ type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC
+ rules:
+ action: DENY
+ policies:
+ api_key_missing:
+ permissions:
+ - any: true
+ principals:
+ - not_id:
+ or_ids:
+ ids:
+ - header:
+ name: apikey
+ present_match: true
+ - header:
+ name: ':path'
+ string_match:
+ contains: apikey=
+ api_key_not_valid:
+ permissions:
+ - any: true
+ principals:
+ - not_id:
+ or_ids:
+ ids:
+ - header:
+ name: apikey
+ string_match:
+ exact: anon_key
+ - header:
+ name: apikey
+ string_match:
+ exact: service_key
+ - header:
+ name: apikey
+ string_match:
+ exact: supabase_admin_key
+ - header:
+ name: ':path'
+ string_match:
+ contains: apikey=anon_key
+ - header:
+ name: ':path'
+ string_match:
+ contains: apikey=service_key
+ - header:
+ name: ':path'
+ string_match:
+ contains: apikey=supabase_admin_key
+ - name: envoy.filters.http.lua
+ typed_config:
+ '@type': >-
+ type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
+ source_codes:
+ remove_apikey_and_empty_key_query_parameters:
+ inline_string: |-
+ function envoy_on_request(request_handle)
+ local path = request_handle:headers():get(":path")
+ request_handle
+ :headers()
+ :replace(":path", path:gsub("&=[^&]*", ""):gsub("?=[^&]*$", ""):gsub("?=[^&]*&", "?"):gsub("&apikey=[^&]*", ""):gsub("?apikey=[^&]*$", ""):gsub("?apikey=[^&]*&", "?"))
+ end
+ remove_empty_key_query_parameters:
+ inline_string: |-
+ function envoy_on_request(request_handle)
+ local path = request_handle:headers():get(":path")
+ request_handle
+ :headers()
+ :replace(":path", path:gsub("&=[^&]*", ""):gsub("?=[^&]*$", ""):gsub("?=[^&]*&", "?"))
+ end
+ - name: envoy.filters.http.compressor.brotli
+ typed_config:
+ '@type': >-
+ type.googleapis.com/envoy.extensions.filters.http.compressor.v3.Compressor
+ response_direction_config:
+ common_config:
+ min_content_length: 100
+ content_type:
+ - application/vnd.pgrst.object+json
+ - application/vnd.pgrst.array+json
+ - application/openapi+json
+ - application/geo+json
+ - text/csv
+ - application/vnd.pgrst.plan
+ - application/vnd.pgrst.object
+ - application/vnd.pgrst.array
+ - application/javascript
+ - application/json
+ - application/xhtml+xml
+ - image/svg+xml
+ - text/css
+ - text/html
+ - text/plain
+ - text/xml
+ disable_on_etag_header: true
+ request_direction_config:
+ common_config:
+ enabled:
+ default_value: false
+ runtime_key: request_compressor_enabled
+ compressor_library:
+ name: text_optimized
+ typed_config:
+ '@type': >-
+ type.googleapis.com/envoy.extensions.compression.brotli.compressor.v3.Brotli
+ - name: envoy.filters.http.compressor.gzip
+ typed_config:
+ '@type': >-
+ type.googleapis.com/envoy.extensions.filters.http.compressor.v3.Compressor
+ response_direction_config:
+ common_config:
+ min_content_length: 100
+ content_type:
+ - application/vnd.pgrst.object+json
+ - application/vnd.pgrst.array+json
+ - application/openapi+json
+ - application/geo+json
+ - text/csv
+ - application/vnd.pgrst.plan
+ - application/vnd.pgrst.object
+ - application/vnd.pgrst.array
+ - application/javascript
+ - application/json
+ - application/xhtml+xml
+ - image/svg+xml
+ - text/css
+ - text/html
+ - text/plain
+ - text/xml
+ disable_on_etag_header: true
+ request_direction_config:
+ common_config:
+ enabled:
+ default_value: false
+ runtime_key: request_compressor_enabled
+ compressor_library:
+ name: text_optimized
+ typed_config:
+ '@type': >-
+ type.googleapis.com/envoy.extensions.compression.gzip.compressor.v3.Gzip
+ - name: envoy.filters.http.router
+ typed_config:
+ '@type': >-
+ type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
+ dynamic_stats: false
+ local_reply_config:
+ mappers:
+ - filter:
+ and_filter:
+ filters:
+ - status_code_filter:
+ comparison:
+ value:
+ default_value: 403
+ runtime_key: unused
+ - header_filter:
+ header:
+ name: ':path'
+ string_match:
+ prefix: /customer/v1/privileged/
+ status_code: 401
+ body:
+ inline_string: Unauthorized
+ headers_to_add:
+ - header:
+ key: WWW-Authenticate
+ value: Basic realm="Unknown"
+ - filter:
+ and_filter:
+ filters:
+ - status_code_filter:
+ comparison:
+ value:
+ default_value: 403
+ runtime_key: unused
+ - header_filter:
+ header:
+ name: ':path'
+ string_match:
+ prefix: /metrics/aggregated
+ invert_match: true
+ status_code: 401
+ body_format_override:
+ json_format:
+ message: >-
+ `apikey` request header or query parameter is either
+ missing or invalid. Double check your Supabase `anon`
+ or `service_role` API key.
+ hint: '%RESPONSE_CODE_DETAILS%'
+ json_format_options:
+ sort_properties: false
+ merge_slashes: true
+ route_config:
+ name: route_config_0
+ virtual_hosts:
+ - name: virtual_host_0
+ domains:
+ - '*'
+ typed_per_filter_config:
+ envoy.filters.http.cors:
+ '@type': >-
+ type.googleapis.com/envoy.extensions.filters.http.cors.v3.CorsPolicy
+ allow_origin_string_match:
+ - safe_regex:
+ regex: \*
+ allow_methods: GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS,TRACE,CONNECT
+ allow_headers: apikey,authorization,x-client-info
+ max_age: '3600'
+ routes:
+ - match:
+ path: /health
+ direct_response:
+ status: 200
+ body:
+ inline_string: Healthy
+ typed_per_filter_config: &ref_0
+ envoy.filters.http.rbac:
+ '@type': >-
+ type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBACPerRoute
+ - match:
+ safe_regex:
+ google_re2:
+ max_program_size: 150
+ regex: >-
+ /auth/v1/(verify|callback|authorize|sso/saml/(acs|metadata|slo)|\.well-known/(openid-configuration|jwks\.json))
+ request_headers_to_remove:
+ - apikey
+ - sb-opk
+ route:
+ cluster: gotrue
+ regex_rewrite:
+ pattern:
+ regex: ^/auth/v1
+ substitution: ''
+ retry_policy:
+ num_retries: 3
+ retry_on: 5xx
+ timeout: 35s
+ typed_per_filter_config: *ref_0
+ - match:
+ prefix: /auth/v1/
+ request_headers_to_remove:
+ - apikey
+ - sb-opk
+ route:
+ cluster: gotrue
+ prefix_rewrite: /
+ timeout: 35s
+ - match:
+ prefix: /rest/v1/
+ query_parameters:
+ - name: apikey
+ present_match: true
+ request_headers_to_remove:
+ - apikey
+ - sb-opk
+ route:
+ cluster: postgrest
+ prefix_rewrite: /
+ timeout: 125s
+ typed_per_filter_config:
+ envoy.filters.http.lua:
+ '@type': >-
+ type.googleapis.com/envoy.extensions.filters.http.lua.v3.LuaPerRoute
+ name: remove_apikey_and_empty_key_query_parameters
+ - match:
+ prefix: /rest/v1/
+ request_headers_to_remove:
+ - apikey
+ - sb-opk
+ route:
+ cluster: postgrest
+ prefix_rewrite: /
+ timeout: 125s
+ typed_per_filter_config:
+ envoy.filters.http.lua:
+ '@type': >-
+ type.googleapis.com/envoy.extensions.filters.http.lua.v3.LuaPerRoute
+ name: remove_empty_key_query_parameters
+ - match:
+ prefix: /rest-admin/v1/
+ query_parameters:
+ - name: apikey
+ present_match: true
+ request_headers_to_remove:
+ - apikey
+ - sb-opk
+ route:
+ cluster: postgrest_admin
+ prefix_rewrite: /
+ typed_per_filter_config:
+ envoy.filters.http.lua:
+ '@type': >-
+ type.googleapis.com/envoy.extensions.filters.http.lua.v3.LuaPerRoute
+ name: remove_apikey_and_empty_key_query_parameters
+ - match:
+ prefix: /rest-admin/v1/
+ request_headers_to_remove:
+ - apikey
+ - sb-opk
+ route:
+ cluster: postgrest_admin
+ prefix_rewrite: /
+ - match:
+ path: /graphql/v1
+ request_headers_to_add:
+ header:
+ key: Content-Profile
+ value: graphql_public
+ request_headers_to_remove:
+ - apikey
+ - sb-opk
+ route:
+ cluster: postgrest
+ prefix_rewrite: /rpc/graphql
+ timeout: 125s
+ - match:
+ prefix: /admin/v1/
+ request_headers_to_remove:
+ - sb-opk
+ route:
+ cluster: admin_api
+ prefix_rewrite: /
+ timeout: 600s
+ - match:
+ prefix: /customer/v1/privileged/
+ request_headers_to_remove:
+ - sb-opk
+ route:
+ cluster: admin_api
+ prefix_rewrite: /privileged/
+ typed_per_filter_config:
+ envoy.filters.http.rbac:
+ '@type': >-
+ type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBACPerRoute
+ rbac:
+ rules:
+ action: DENY
+ policies:
+ basic_auth:
+ permissions:
+ - any: true
+ principals:
+ - header:
+ name: authorization
+ invert_match: true
+ string_match:
+ exact: Basic c2VydmljZV9yb2xlOnNlcnZpY2Vfa2V5
+ treat_missing_header_as_empty: true
+ - match:
+ prefix: /metrics/aggregated
+ request_headers_to_remove:
+ - sb-opk
+ route:
+ cluster: admin_api
+ prefix_rewrite: /supabase-internal/metrics
+ typed_per_filter_config:
+ envoy.filters.http.rbac:
+ '@type': >-
+ type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBACPerRoute
+ rbac:
+ rules:
+ action: DENY
+ policies:
+ not_private_ip:
+ permissions:
+ - any: true
+ principals:
+ - not_id:
+ direct_remote_ip:
+ address_prefix: 10.0.0.0
+ prefix_len: 8
+ include_attempt_count_in_response: true
+ retry_policy:
+ num_retries: 5
+ retry_back_off:
+ base_interval: 0.1s
+ max_interval: 1s
+ retry_on: gateway-error
+ stat_prefix: ingress_http
+ - '@type': type.googleapis.com/envoy.config.listener.v3.Listener
+ name: https_listener
+ address:
+ socket_address:
+ address: '::'
+ port_value: 443
+ ipv4_compat: true
+ filter_chains:
+ - filters: *ref_1
+ transport_socket:
+ name: envoy.transport_sockets.tls
+ typed_config:
+ '@type': >-
+ type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
+ common_tls_context:
+ tls_certificates:
+ - certificate_chain:
+ filename: /etc/envoy/fullChain.pem
+ private_key:
+ filename: /etc/envoy/privKey.pem
+
diff --git a/ansible/tasks/setup-supabase-internal.yml b/ansible/tasks/setup-supabase-internal.yml
index aea3a78c..37e97dc5 100644
--- a/ansible/tasks/setup-supabase-internal.yml
+++ b/ansible/tasks/setup-supabase-internal.yml
@@ -114,3 +114,6 @@
import_tasks: internal/install-salt.yml
tags:
- aws-only
+
+- name: Envoy - use lds.supabase.yml for /etc/envoy/lds.yaml
+ command: mv /etc/envoy/lds.supabase.yml /etc/envoy/lds.yaml
diff --git a/ansible/vars.yml b/ansible/vars.yml
index d059b5f5..26416e8d 100644
--- a/ansible/vars.yml
+++ b/ansible/vars.yml
@@ -11,8 +11,8 @@ postgres_major:
# Full version strings for each major version
postgres_release:
- postgres15: "15.8.1.010"
- postgres16: "16.3.1.016"
+ postgres15: "15.8.1.011"
+ postgres16: "16.3.1.017"
# Non Postgres Extensions
pgbouncer_release: "1.19.0"
diff --git a/docker/all-in-one/Dockerfile b/docker/all-in-one/Dockerfile
index 70082bfb..d9e5f3fa 100644
--- a/docker/all-in-one/Dockerfile
+++ b/docker/all-in-one/Dockerfile
@@ -227,9 +227,12 @@ COPY docker/all-in-one/etc/gotrue.env /etc/gotrue.env
# Customizations for envoy
ARG envoy_release
+ARG envoy_lds="lds.yaml"
ADD --chmod=755 --chown=envoy:envoy "https://raw.githubusercontent.com/envoyproxy/envoy/v${envoy_release}/restarter/hot-restarter.py" /opt/envoy-hot-restarter.py
COPY --chmod=775 --chown=envoy:envoy ansible/files/envoy_config/ /etc/envoy/
COPY --chmod=755 --chown=envoy:envoy ansible/files/start-envoy.sh /opt/
+RUN mv /etc/envoy/${envoy_lds} /etc/envoy/lds.yaml
+RUN rm -f /etc/envoy/lds.supabase.yaml
# Customizations for kong
COPY docker/all-in-one/etc/kong/kong.conf /etc/kong/kong.conf
[parent: ffa105240051]