feat(security): harden gateway boundaries and usage policies

Consolidate subscription usage policy enforcement, privacy-safe persistence, and gateway security hardening into one reviewable change.

Includes bounded HTTP and execution envelopes, header and protocol guards, DNS and relay validation, authentication and secret projection hardening, secure backup/install paths, and regression coverage.
This commit is contained in:
elky
2026-09-04 03:45:52 +08:00
parent ddcbeb3ae9
commit 579f2c7cc1
1019 changed files with 190437 additions and 26080 deletions
@@ -12,6 +12,7 @@ CREATE TABLE IF NOT EXISTS public.users (
email_verified boolean DEFAULT false NOT NULL,
is_active boolean DEFAULT true NOT NULL,
is_deleted boolean DEFAULT false NOT NULL,
security_version bigint DEFAULT 0 NOT NULL,
allowed_models jsonb,
allowed_models_mode character varying(32) DEFAULT 'unrestricted' NOT NULL,
allowed_providers jsonb,
@@ -202,6 +203,7 @@ CREATE INDEX IF NOT EXISTS user_preferences_user_id_idx ON public.user_preferenc
CREATE TABLE IF NOT EXISTS public.user_sessions (
id character varying(64) NOT NULL,
user_id character varying(64) NOT NULL,
security_version bigint DEFAULT 0 NOT NULL,
client_device_id character varying(128) NOT NULL,
device_label character varying(120),
device_type character varying(20) DEFAULT 'unknown' NOT NULL,
@@ -48,6 +48,7 @@ ALTER TABLE ONLY public.oauth_providers ADD CONSTRAINT oauth_providers_pkey PRIM
CREATE TABLE IF NOT EXISTS public.ldap_configs (
id bigserial NOT NULL,
singleton_key integer DEFAULT 1 NOT NULL,
server_url character varying(255) NOT NULL,
bind_dn text NOT NULL,
bind_password_encrypted text,
@@ -65,6 +66,7 @@ CREATE TABLE IF NOT EXISTS public.ldap_configs (
);
ALTER TABLE ONLY public.ldap_configs ADD CONSTRAINT ldap_configs_pkey PRIMARY KEY (id);
ALTER TABLE ONLY public.ldap_configs ADD CONSTRAINT ldap_configs_singleton_key_key UNIQUE (singleton_key);
CREATE TABLE IF NOT EXISTS public.user_oauth_links (
id character varying(64) NOT NULL,
@@ -3,6 +3,7 @@
CREATE TABLE IF NOT EXISTS public.proxy_nodes (
id character varying(64) NOT NULL,
tunnel_generation character varying(64) NOT NULL,
name character varying(255) NOT NULL,
ip character varying(512) NOT NULL,
port integer NOT NULL,
@@ -17,7 +18,7 @@ CREATE TABLE IF NOT EXISTS public.proxy_nodes (
is_manual boolean DEFAULT false NOT NULL,
proxy_url character varying(500),
proxy_username character varying(255),
proxy_password character varying(500),
proxy_password text,
created_at bigint NOT NULL,
updated_at bigint NOT NULL,
remote_config jsonb,
@@ -34,6 +35,7 @@ CREATE TABLE IF NOT EXISTS public.proxy_nodes (
);
ALTER TABLE ONLY public.proxy_nodes ADD CONSTRAINT proxy_nodes_pkey PRIMARY KEY (id);
ALTER TABLE ONLY public.proxy_nodes ADD CONSTRAINT uq_proxy_node_ip_port UNIQUE (ip, port);
CREATE TABLE IF NOT EXISTS public.proxy_node_events (
id bigserial NOT NULL,
@@ -105,6 +105,7 @@ CREATE INDEX IF NOT EXISTS idx_payment_orders_wallet_created ON public.payment_o
CREATE INDEX IF NOT EXISTS idx_payment_orders_user_created ON public.payment_orders USING btree (user_id, created_at);
CREATE INDEX IF NOT EXISTS idx_payment_orders_status ON public.payment_orders USING btree (status);
CREATE INDEX IF NOT EXISTS idx_payment_orders_gateway_order_id ON public.payment_orders USING btree (gateway_order_id);
CREATE UNIQUE INDEX IF NOT EXISTS uq_payment_orders_payment_method_gateway_order_id ON public.payment_orders USING btree (payment_method, gateway_order_id);
CREATE INDEX IF NOT EXISTS idx_payment_orders_kind_status ON public.payment_orders USING btree (order_kind, status);
CREATE INDEX IF NOT EXISTS idx_payment_orders_product ON public.payment_orders USING btree (product_id);
@@ -137,8 +138,6 @@ ALTER TABLE ONLY public.user_referrals ADD CONSTRAINT user_referrals_invitee_use
CREATE INDEX IF NOT EXISTS idx_user_referrals_inviter ON public.user_referrals USING btree (inviter_user_id, created_at);
CREATE INDEX IF NOT EXISTS idx_user_referrals_created ON public.user_referrals USING btree (created_at);
CREATE INDEX IF NOT EXISTS idx_user_referrals_invite_code ON public.user_referrals USING btree (invite_code_snapshot);
ALTER TABLE ONLY public.user_referrals ADD CONSTRAINT user_referrals_inviter_user_id_fkey FOREIGN KEY (inviter_user_id) REFERENCES public.users(id) ON DELETE CASCADE;
ALTER TABLE ONLY public.user_referrals ADD CONSTRAINT user_referrals_invitee_user_id_fkey FOREIGN KEY (invitee_user_id) REFERENCES public.users(id) ON DELETE CASCADE;
ALTER TABLE ONLY public.user_referrals ADD CONSTRAINT user_referrals_first_paid_order_fkey FOREIGN KEY (first_paid_order_id) REFERENCES public.payment_orders(id) ON DELETE SET NULL;
CREATE TABLE IF NOT EXISTS public.referral_rewards (
@@ -169,8 +168,6 @@ CREATE INDEX IF NOT EXISTS idx_referral_rewards_inviter_created ON public.referr
CREATE INDEX IF NOT EXISTS idx_referral_rewards_created ON public.referral_rewards USING btree (created_at);
CREATE INDEX IF NOT EXISTS idx_referral_rewards_source_order ON public.referral_rewards USING btree (source_order_id);
ALTER TABLE ONLY public.referral_rewards ADD CONSTRAINT referral_rewards_referral_id_fkey FOREIGN KEY (referral_id) REFERENCES public.user_referrals(id) ON DELETE CASCADE;
ALTER TABLE ONLY public.referral_rewards ADD CONSTRAINT referral_rewards_inviter_user_id_fkey FOREIGN KEY (inviter_user_id) REFERENCES public.users(id) ON DELETE CASCADE;
ALTER TABLE ONLY public.referral_rewards ADD CONSTRAINT referral_rewards_invitee_user_id_fkey FOREIGN KEY (invitee_user_id) REFERENCES public.users(id) ON DELETE CASCADE;
ALTER TABLE ONLY public.referral_rewards ADD CONSTRAINT referral_rewards_source_order_fkey FOREIGN KEY (source_order_id) REFERENCES public.payment_orders(id) ON DELETE SET NULL;
CREATE TABLE IF NOT EXISTS public.payment_gateway_configs (
@@ -177,6 +177,7 @@ CREATE TABLE IF NOT EXISTS public.usage_counter_deltas (
request_id character varying(128) NOT NULL,
kind character varying(64) NOT NULL,
target_id text NOT NULL,
target_tunnel_generation character varying(64),
request_count_delta bigint DEFAULT 0 NOT NULL,
total_requests_delta bigint DEFAULT 0 NOT NULL,
success_count_delta bigint DEFAULT 0 NOT NULL,
@@ -249,3 +250,41 @@ CREATE INDEX IF NOT EXISTS usage_settlement_snapshots_wallet_id_idx ON public.us
CREATE INDEX IF NOT EXISTS ix_usage_settlement_snapshots_schema_version ON public.usage_settlement_snapshots USING btree (settlement_snapshot_schema_version);
CREATE INDEX IF NOT EXISTS ix_usage_settlement_snapshots_pricing_source ON public.usage_settlement_snapshots USING btree (billing_pricing_source);
CREATE TABLE IF NOT EXISTS public.usage_cost_reservations (
request_id character varying(128) NOT NULL,
subject_id character varying(128) NOT NULL,
reservation_token character varying(128) NOT NULL,
admitted_at timestamp with time zone NOT NULL,
reserved_cost_units bigint NOT NULL,
actual_cost_units bigint,
state character varying(20) NOT NULL,
reservation_expires_at timestamp with time zone NOT NULL,
retain_until timestamp with time zone NOT NULL,
finalized_at timestamp with time zone,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL
);
ALTER TABLE ONLY public.usage_cost_reservations ADD CONSTRAINT usage_cost_reservations_pkey PRIMARY KEY (reservation_token);
CREATE INDEX IF NOT EXISTS usage_cost_reservations_request_id_idx ON public.usage_cost_reservations USING btree (request_id);
CREATE INDEX IF NOT EXISTS usage_cost_reservations_subject_admitted_at_idx ON public.usage_cost_reservations USING btree (subject_id, admitted_at);
CREATE INDEX IF NOT EXISTS usage_cost_reservations_reservation_expires_at_idx ON public.usage_cost_reservations USING btree (reservation_expires_at);
CREATE INDEX IF NOT EXISTS usage_cost_reservations_retain_until_token_idx ON public.usage_cost_reservations USING btree (retain_until, reservation_token);
ALTER TABLE ONLY public.usage_cost_reservations ADD CONSTRAINT usage_cost_reservations_subject_id_fkey FOREIGN KEY (subject_id) REFERENCES public.users(id) ON DELETE CASCADE;
CREATE TABLE IF NOT EXISTS public.usage_request_admissions (
request_id character varying(128) NOT NULL,
subject_id character varying(128) NOT NULL,
event_token character varying(128) NOT NULL,
admitted_at timestamp with time zone NOT NULL,
retain_until timestamp with time zone NOT NULL,
state character varying(20) NOT NULL,
released_at timestamp with time zone,
created_at timestamp with time zone NOT NULL
);
ALTER TABLE ONLY public.usage_request_admissions ADD CONSTRAINT usage_request_admissions_pkey PRIMARY KEY (event_token);
CREATE INDEX IF NOT EXISTS usage_request_admissions_subject_admitted_at_idx ON public.usage_request_admissions USING btree (subject_id, admitted_at);
CREATE INDEX IF NOT EXISTS usage_request_admissions_retain_until_token_idx ON public.usage_request_admissions USING btree (retain_until, event_token);
ALTER TABLE ONLY public.usage_request_admissions ADD CONSTRAINT usage_request_admissions_subject_id_fkey FOREIGN KEY (subject_id) REFERENCES public.users(id) ON DELETE CASCADE;