From 2e5f727da829f90b3a67ebe903ccf80b390b3cb7 Mon Sep 17 00:00:00 2001 From: Yvan Sraka Date: Tue, 12 Aug 2025 12:19:14 +0200 Subject: [PATCH 1/2] Deploy envoy using system manager We verify that the envoy service can be deployed using the system manager. --- nix/packages/default.nix | 1 + nix/packages/envoy-bin.nix | 30 ++++++++++++++ nix/systemConfigs.nix | 60 +++++++++++++-------------- nix/systemModules/default.nix | 4 +- nix/systemModules/envoy.nix | 24 +++++++++++ nix/systemModules/tests/test_envoy.py | 3 ++ 6 files changed, 91 insertions(+), 31 deletions(-) create mode 100644 nix/packages/envoy-bin.nix create mode 100644 nix/systemModules/envoy.nix create mode 100644 nix/systemModules/tests/test_envoy.py diff --git a/nix/packages/default.nix b/nix/packages/default.nix index bdb5cd112..320f4a16f 100644 --- a/nix/packages/default.nix +++ b/nix/packages/default.nix @@ -38,6 +38,7 @@ dbmate-tool = pkgs.callPackage ./dbmate-tool.nix { inherit (self.supabase) defaults; }; docker-image-ubuntu = pkgs.callPackage ./docker-ubuntu.nix { }; docs = pkgs.callPackage ./docs.nix { }; + envoy-bin = pkgs.callPackage ./envoy-bin.nix { }; supabase-groonga = pkgs.callPackage ./groonga { }; local-infra-bootstrap = pkgs.callPackage ./local-infra-bootstrap.nix { }; migrate-tool = pkgs.callPackage ./migrate-tool.nix { psql_15 = self'.packages."psql_15/bin"; }; diff --git a/nix/packages/envoy-bin.nix b/nix/packages/envoy-bin.nix new file mode 100644 index 000000000..b26494a79 --- /dev/null +++ b/nix/packages/envoy-bin.nix @@ -0,0 +1,30 @@ +{ + envoy-bin, + fetchurl, + stdenv, + ... +}: +let + version = "1.28.0"; + inherit (stdenv.hostPlatform) system; + throwSystem = throw "envoy-bin is not available for ${system}."; + plat = + { + aarch64-linux = "aarch_64"; + x86_64-linux = "x86_64"; + } + .${system} or throwSystem; + hash = + { + aarch64-linux = "sha256-65MOMqtVVWQ+CdEdSQ45LQp5DFqA6wsOussQRr27EU0="; + x86_64-linux = "sha256-JjlWPOm8CbHua9RzF2C1lsjtHkdM3YPMnfk2RRbhQ2c="; + } + .${system} or throwSystem; +in +envoy-bin.overrideAttrs { + inherit version; + src = fetchurl { + url = "https://github.com/envoyproxy/envoy/releases/download/v${version}/envoy-${version}-linux-${plat}"; + inherit hash; + }; +} diff --git a/nix/systemConfigs.nix b/nix/systemConfigs.nix index 7f50ded93..3b8e76da4 100644 --- a/nix/systemConfigs.nix +++ b/nix/systemConfigs.nix @@ -1,30 +1,30 @@ -{ self, inputs, ... }: -let - mkModules = system: [ - ({ - services.nginx.enable = true; - nixpkgs.hostPlatform = system; - }) - ]; - - systems = [ - "aarch64-linux" - "x86_64-linux" - ]; - - mkSystemConfig = system: { - name = system; - value.default = inputs.system-manager.lib.makeSystemConfig { - modules = mkModules system; - extraSpecialArgs = { - inherit self; - inherit system; - }; - }; - }; -in -{ - flake = { - systemConfigs = builtins.listToAttrs (map mkSystemConfig systems); - }; -} + { self, inputs, ... }: + let + mkModules = system: [ + ({ + services.nginx.enable = true; + nixpkgs.hostPlatform = system; + }) + ]; + + systems = [ + "aarch64-linux" + "x86_64-linux" + ]; + + mkSystemConfig = system: { + name = system; + value.default = inputs.system-manager.lib.makeSystemConfig { + modules = mkModules system; + extraSpecialArgs = { + inherit self; + inherit system; + }; + }; + }; + in + { + flake = { + systemConfigs = builtins.listToAttrs (map mkSystemConfig systems); + }; + } diff --git a/nix/systemModules/default.nix b/nix/systemModules/default.nix index 14b459255..dfd30e666 100644 --- a/nix/systemModules/default.nix +++ b/nix/systemModules/default.nix @@ -4,6 +4,8 @@ { imports = [ ./tests ]; flake = { - systemModules = { }; + systemModules = { + envoy = ./envoy.nix; + }; }; } diff --git a/nix/systemModules/envoy.nix b/nix/systemModules/envoy.nix new file mode 100644 index 000000000..4451f79ff --- /dev/null +++ b/nix/systemModules/envoy.nix @@ -0,0 +1,24 @@ +{ + lib, + nixosModulesPath, + self, + system, + ... +}: +{ + imports = map (path: nixosModulesPath + path) [ + "/services/networking/envoy.nix" + ]; + config = { + services.envoy = { + enable = true; + package = self.packages.${system}.envoy-bin; + # TODO: settings from postgres/ansible/files/envoy_config/ + }; + systemd.services.envoy = { + wantedBy = lib.mkForce [ + "system-manager.target" + ]; + }; + }; +} diff --git a/nix/systemModules/tests/test_envoy.py b/nix/systemModules/tests/test_envoy.py new file mode 100644 index 000000000..ee9759940 --- /dev/null +++ b/nix/systemModules/tests/test_envoy.py @@ -0,0 +1,3 @@ +def test_envoy_service(host): + assert host.service("envoy.service").is_valid + assert host.service("envoy.service").is_running From 400a378982732f8d6be417b3235c29b7712db5d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Roche?= Date: Tue, 19 Aug 2025 15:56:32 +0200 Subject: [PATCH 2/2] WIP: envoy configuration --- nix/systemConfigs.nix | 65 +- nix/systemModules/default.nix | 2 +- nix/systemModules/envoy.nix | 24 - nix/systemModules/envoy/access_log.nix | 19 + nix/systemModules/envoy/admin_api.nix | 125 ++ nix/systemModules/envoy/default.nix | 125 ++ nix/systemModules/envoy/http_filters.nix | 216 +++ nix/systemModules/envoy/listeners.nix | 1298 +++++++++++++++++ .../envoy/local_reply_config.nix | 91 ++ nix/systemModules/envoy/route_config.nix | 2 + nix/systemModules/tests/test_envoy.py | 2 +- 11 files changed, 1913 insertions(+), 56 deletions(-) delete mode 100644 nix/systemModules/envoy.nix create mode 100644 nix/systemModules/envoy/access_log.nix create mode 100644 nix/systemModules/envoy/admin_api.nix create mode 100644 nix/systemModules/envoy/default.nix create mode 100644 nix/systemModules/envoy/http_filters.nix create mode 100644 nix/systemModules/envoy/listeners.nix create mode 100644 nix/systemModules/envoy/local_reply_config.nix create mode 100644 nix/systemModules/envoy/route_config.nix diff --git a/nix/systemConfigs.nix b/nix/systemConfigs.nix index 3b8e76da4..fe82fb5ef 100644 --- a/nix/systemConfigs.nix +++ b/nix/systemConfigs.nix @@ -1,30 +1,35 @@ - { self, inputs, ... }: - let - mkModules = system: [ - ({ - services.nginx.enable = true; - nixpkgs.hostPlatform = system; - }) - ]; - - systems = [ - "aarch64-linux" - "x86_64-linux" - ]; - - mkSystemConfig = system: { - name = system; - value.default = inputs.system-manager.lib.makeSystemConfig { - modules = mkModules system; - extraSpecialArgs = { - inherit self; - inherit system; - }; - }; - }; - in - { - flake = { - systemConfigs = builtins.listToAttrs (map mkSystemConfig systems); - }; - } +{ self, inputs, ... }: +let + mkModules = system: [ + self.systemModules.envoy + ({ + services.nginx.enable = true; + nixpkgs.hostPlatform = system; + supabase.services.envoy = { + enable = true; + enableTLS = true; + }; + }) + ]; + + systems = [ + "aarch64-linux" + "x86_64-linux" + ]; + + mkSystemConfig = system: { + name = system; + value.default = inputs.system-manager.lib.makeSystemConfig { + modules = mkModules system; + extraSpecialArgs = { + inherit self; + inherit system; + }; + }; + }; +in +{ + flake = { + systemConfigs = builtins.listToAttrs (map mkSystemConfig systems); + }; +} diff --git a/nix/systemModules/default.nix b/nix/systemModules/default.nix index dfd30e666..3219c90bf 100644 --- a/nix/systemModules/default.nix +++ b/nix/systemModules/default.nix @@ -5,7 +5,7 @@ imports = [ ./tests ]; flake = { systemModules = { - envoy = ./envoy.nix; + envoy = ./envoy; }; }; } diff --git a/nix/systemModules/envoy.nix b/nix/systemModules/envoy.nix deleted file mode 100644 index 4451f79ff..000000000 --- a/nix/systemModules/envoy.nix +++ /dev/null @@ -1,24 +0,0 @@ -{ - lib, - nixosModulesPath, - self, - system, - ... -}: -{ - imports = map (path: nixosModulesPath + path) [ - "/services/networking/envoy.nix" - ]; - config = { - services.envoy = { - enable = true; - package = self.packages.${system}.envoy-bin; - # TODO: settings from postgres/ansible/files/envoy_config/ - }; - systemd.services.envoy = { - wantedBy = lib.mkForce [ - "system-manager.target" - ]; - }; - }; -} diff --git a/nix/systemModules/envoy/access_log.nix b/nix/systemModules/envoy/access_log.nix new file mode 100644 index 000000000..d1ffefcb8 --- /dev/null +++ b/nix/systemModules/envoy/access_log.nix @@ -0,0 +1,19 @@ +[ + { + 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"; + }; + } +] diff --git a/nix/systemModules/envoy/admin_api.nix b/nix/systemModules/envoy/admin_api.nix new file mode 100644 index 000000000..b563d0e39 --- /dev/null +++ b/nix/systemModules/envoy/admin_api.nix @@ -0,0 +1,125 @@ +{ + config = { + name = "admin_api"; + load_assignment = { + cluster_name = "admin_api"; + endpoints = [ + { + lb_endpoints = [ + { + endpoint = { + address = { + socket_address = { + address = "127.0.0.1"; + port_value = 8085; + }; + }; + }; + } + ]; + } + ]; + }; + circuit_breakers = { + thresholds = [ + { + priority = "DEFAULT"; + max_connections = 10000; + max_pending_requests = 10000; + max_requests = 10000; + retry_budget = { + budget_percent = { + value = 100; + }; + min_retry_concurrency = 100; + }; + } + ]; + }; + }; + routes = [ + { + 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; + }; + }; + } + ]; + }; + }; + }; + }; + }; + }; + } + ]; +} diff --git a/nix/systemModules/envoy/default.nix b/nix/systemModules/envoy/default.nix new file mode 100644 index 000000000..b80b1e631 --- /dev/null +++ b/nix/systemModules/envoy/default.nix @@ -0,0 +1,125 @@ +{ + lib, + nixosModulesPath, + self, + system, + config, + ... +}: +let + cfg = config.supabase.services.envoy; + services = [ (import ./admin_api.nix) ]; + mkFilters = services: [ + { + name = "envoy.filters.network.http_connection_manager"; + typed_config = { + "@type" = + "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager"; + access_log = import ./access_log.nix; + generate_request_id = false; + http_filters = import ./http_filters.nix; + local_reply_config = import ./local_reply_config.nix; + merge_slashes = true; + route_config = import ./route_config.nix { + inherit services; + }; + stat_prefix = "ingress_http"; + }; + } + ]; + filters = mkFilters services; +in +{ + imports = map (path: nixosModulesPath + path) [ + "/services/networking/envoy.nix" + ]; + + options = { + supabase.services.envoy = { + enable = lib.mkEnableOption "Envoy proxy"; + enableTLS = lib.mkOption { + type = lib.types.bool; + default = true; + description = '' + Whether to enable TLS support in Envoy. + If enabled, you must provide the TLS certificate and key files. + ''; + }; + }; + }; + + config = lib.mkIf cfg.enable { + services.envoy = { + enable = true; + package = self.packages.${system}.envoy-bin; + # We don't validate the config at build time if TLS is enabled, + # because it requires the TLS certificate and key files to be present. + requireValidConfig = !cfg.enableTLS; + settings = { + node = { + cluster = "cluster_0"; + id = "node_0"; + }; + stats_config = { + stats_matcher = { + reject_all = true; + }; + }; + static_resources = { + clusters = map (cluster: cluster.config) services; + listeners = [ + { + name = "http_listener"; + address = { + socket_address = { + address = "::"; + port_value = 80; + ipv4_compat = true; + }; + }; + filter_chains = { + inherit filters; + }; + } + (lib.mkIf cfg.enableTLS { + name = "https_listener"; + address = { + socket_address = { + address = "::"; + port_value = 443; + ipv4_compat = true; + }; + }; + filter_chains = { + inherit filters; + 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"; + }; + } + ]; + }; + }; + }; + }; + }) + ]; + }; + }; + }; + systemd.services.envoy = { + wantedBy = lib.mkForce [ + "system-manager.target" + ]; + }; + }; +} diff --git a/nix/systemModules/envoy/http_filters.nix b/nix/systemModules/envoy/http_filters.nix new file mode 100644 index 000000000..dabc96622 --- /dev/null +++ b/nix/systemModules/envoy/http_filters.nix @@ -0,0 +1,216 @@ +[ + { + 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)\n local path = request_handle:headers():get(\":path\")\n request_handle\n :headers()\n :replace(\":path\", path:gsub(\"&=[^&]*\", \"\"):gsub(\"?=[^&]*$\", \"\"):gsub(\"?=[^&]*&\", \"?\"):gsub(\"&apikey=[^&]*\", \"\"):gsub(\"?apikey=[^&]*$\", \"\"):gsub(\"?apikey=[^&]*&\", \"?\"))\nend"; + }; + remove_empty_key_query_parameters = { + inline_string = "function envoy_on_request(request_handle)\n local path = request_handle:headers():get(\":path\")\n request_handle\n :headers()\n :replace(\":path\", path:gsub(\"&=[^&]*\", \"\"):gsub(\"?=[^&]*$\", \"\"):gsub(\"?=[^&]*&\", \"?\"))\nend"; + }; + }; + }; + } + { + 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; + }; + } +] diff --git a/nix/systemModules/envoy/listeners.nix b/nix/systemModules/envoy/listeners.nix new file mode 100644 index 000000000..01d4073c9 --- /dev/null +++ b/nix/systemModules/envoy/listeners.nix @@ -0,0 +1,1298 @@ +{ + listeners = [ + { + "@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 = [ + { + 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)\n local path = request_handle:headers():get(\":path\")\n request_handle\n :headers()\n :replace(\":path\", path:gsub(\"&=[^&]*\", \"\"):gsub(\"?=[^&]*$\", \"\"):gsub(\"?=[^&]*&\", \"?\"):gsub(\"&apikey=[^&]*\", \"\"):gsub(\"?apikey=[^&]*$\", \"\"):gsub(\"?apikey=[^&]*&\", \"?\"))\nend"; + }; + remove_empty_key_query_parameters = { + inline_string = "function envoy_on_request(request_handle)\n local path = request_handle:headers():get(\":path\")\n request_handle\n :headers()\n :replace(\":path\", path:gsub(\"&=[^&]*\", \"\"):gsub(\"?=[^&]*$\", \"\"):gsub(\"?=[^&]*&\", \"?\"))\nend"; + }; + }; + }; + } + { + 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; + headers_to_add = [ + { + header = { + key = "x-sb-error-code"; + value = "%RESPONSE_CODE_DETAILS%"; + }; + } + ]; + 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 = { + "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 = { + "envoy.filters.http.rbac" = { + "@type" = "type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBACPerRoute"; + }; + }; + } + { + 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 = [ + { + 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)\n local path = request_handle:headers():get(\":path\")\n request_handle\n :headers()\n :replace(\":path\", path:gsub(\"&=[^&]*\", \"\"):gsub(\"?=[^&]*$\", \"\"):gsub(\"?=[^&]*&\", \"?\"):gsub(\"&apikey=[^&]*\", \"\"):gsub(\"?apikey=[^&]*$\", \"\"):gsub(\"?apikey=[^&]*&\", \"?\"))\nend"; + }; + remove_empty_key_query_parameters = { + inline_string = "function envoy_on_request(request_handle)\n local path = request_handle:headers():get(\":path\")\n request_handle\n :headers()\n :replace(\":path\", path:gsub(\"&=[^&]*\", \"\"):gsub(\"?=[^&]*$\", \"\"):gsub(\"?=[^&]*&\", \"?\"))\nend"; + }; + }; + }; + } + { + 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; + headers_to_add = [ + { + header = { + key = "x-sb-error-code"; + value = "%RESPONSE_CODE_DETAILS%"; + }; + } + ]; + 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 = { + "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 = { + "envoy.filters.http.rbac" = { + "@type" = "type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBACPerRoute"; + }; + }; + } + { + 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"; + }; + } + ]; + 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/nix/systemModules/envoy/local_reply_config.nix b/nix/systemModules/envoy/local_reply_config.nix new file mode 100644 index 000000000..b3b01a9c0 --- /dev/null +++ b/nix/systemModules/envoy/local_reply_config.nix @@ -0,0 +1,91 @@ +{ + 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; + headers_to_add = [ + { + header = { + key = "x-sb-error-code"; + value = "%RESPONSE_CODE_DETAILS%"; + }; + } + ]; + 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; + }; + }; + } + ]; +} diff --git a/nix/systemModules/envoy/route_config.nix b/nix/systemModules/envoy/route_config.nix new file mode 100644 index 000000000..95e289f41 --- /dev/null +++ b/nix/systemModules/envoy/route_config.nix @@ -0,0 +1,2 @@ +{ services }: +{} diff --git a/nix/systemModules/tests/test_envoy.py b/nix/systemModules/tests/test_envoy.py index ee9759940..b92bc528e 100644 --- a/nix/systemModules/tests/test_envoy.py +++ b/nix/systemModules/tests/test_envoy.py @@ -1,3 +1,3 @@ def test_envoy_service(host): assert host.service("envoy.service").is_valid - assert host.service("envoy.service").is_running + assert host.service("envoy.service").is_running, "Envoy service should be running but failed: {}".format(host.run("systemctl status envoy.service").stdout)